Basic usage

On the client, set hx-encoding to multipart/form-data:

<form hx-post="/upload" hx-encoding="multipart/form-data"> <input type="file" name="file"> <input type="text" name="name"> <button>Submit</button> </form>

On the server, respond with a success or error message:

<p>File uploaded successfully.</p>

Preserving file selection on errors

When a form re-renders with validation errors, file inputs lose their selection. Add hx-preserve to keep it:

<form hx-post="..." hx-swap="outerHTML" hx-encoding="multipart/form-data"> <input hx-preserve type="file" name="file"> <input type="text" name="name"> <button>Submit</button> </form>

Try it in the demo: select a file, then submit with empty fields. The form re-renders with errors, but your file selection stays.

Alternatively, place the file input outside the swap target using the form attribute:

<input form="my-form" type="file" name="file"> <form id="my-form" hx-post="..." hx-encoding="multipart/form-data"> <button>Submit</button> </form>

The input is outside the form element, so it is never replaced during swaps.

Upload progress

htmx 4 sends requests with the native fetch API, which, sadly, does not report upload progress.

If you want more fully featured upload consider one of the following JavaScript libraries: