htmx: Send files using htmx.ajax()

Join the AI Workshop to learn more about AI and how it can be applied to web development. Next cohort February 1st, 2026

The AI-first Web Development BOOTCAMP cohort starts February 24th, 2026. 10 weeks of intensive training and hands-on projects.


In a drag and drop file upload workflow I had the need to use htmx.ajax() to fire a network call to upload files, but I had some trouble sending data to the server.

After some research I found out using htmx.ajax() you cannot (at the time of writing) set the Content-Type header to multipart/form-data, which is what makes file uploads possible in HTTP.

To overcome this, you must set the hx-encoding attribute in the HTML, and use the source property in the context object passed to htmx.ajax():

<div
  ...
  hx-post={`/upload`}
  hx-encoding="multipart/form-data"
  hx-trigger="none"
>
htmx.ajax('POST',
  event.currentTarget.getAttribute('hx-post'), {
  values: {
    files: formData.getAll('files')
  },
  source: event.currentTarget,
})

Lessons in this unit:

0: Introduction
1: Why htmx
2: The core idea of htmx
3: Installing htmx
4: Doing a GET request
5: Swap
6: POST request
7: Targets
8: Loading indicator
9: Confirming actions, and prompts
10: Triggers
11: Request headers
12: Response headers
13: Events
14: Redirect after request
15: ▶︎ Send files using htmx.ajax()
16: Perform something on page load
17: Conditionally hide HTML elements based on HTMX request status
18: htmx + Alpine template tag
19: htmx, include hidden input fields outside of a form
20: htmx trigger request via JS event