# File Upload
<div id="demo-content" class="not-prose demo-container flex items-center justify-center min-h-[460px]"></div>
## Basic usage
On the client, set `hx-encoding` to `multipart/form-data`:
```html
<form hx-post="/upload"
hx-encoding="multipart/form-data">
<input type="file" name="file">
<input type="text" name="name">
<button>Submit</button>
</form>
```
- [`hx-encoding`](https://four.htmx.org/reference/attributes/hx-encoding)=`"multipart/form-data"` sends the form as [`FormData`](https://developer.mozilla.org/en-US/docs/Web/API/FormData), required for file uploads.
- [`hx-post`](https://four.htmx.org/reference/attributes/hx-post) submits the form to `/upload`.
On the server, respond with a success or error message:
```html
<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`](https://four.htmx.org/reference/attributes/hx-preserve) to keep it:
```html
<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](https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Elements/input#form):
```html
<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.x uses the native [`fetch`](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API) API. `fetch` supports [upload progress monitoring](https://developer.mozilla.org/en-US/docs/Web/API/Fetch_API/Using_Fetch#monitoring_upload_progress) in some browsers, but cross-browser support is limited. For reliable progress tracking, use [`XMLHttpRequest`](https://developer.mozilla.org/en-US/docs/Web/API/XMLHttpRequest/upload) directly:
```html
<form id="upload-form" enctype="multipart/form-data">
<input type="file" name="file">
<button>Upload</button>
<progress id="progress" value="0" max="100"></progress>
</form>
<script>
document.querySelector('#upload-form').addEventListener('submit', (e) => {
e.preventDefault();
const xhr = new XMLHttpRequest();
xhr.upload.addEventListener('progress', (evt) => {
document.querySelector('#progress').value = (evt.loaded / evt.total) * 100;
});
xhr.open('POST', '/upload');
xhr.send(new FormData(e.target));
});
</script>
```
htmx 4.x uses the native fetch API. fetch supports upload progress monitoring in some browsers, but cross-browser support is limited. For reliable progress tracking, use XMLHttpRequest directly: