---
title: "Check files in the browser before they · Agent Wranglers"
url: https://agentwranglers.se/playbook/app-ui/forms/file-upload-field
updated: 2026-10-21
licence: "CC BY 4.0"
licence_url: https://creativecommons.org/licenses/by/4.0/
---

# Check files in the browser before they upload

By [Agent Wranglers](https://agentwranglers.se/authors/agent-wranglers). Updated 2026-10-21.

For AI agents: [llms.txt](https://agentwranglers.se/llms.txt) is a short guide that sends you to the rules your problem needs, and [rules.json](https://agentwranglers.se/playbook/app-ui/rules.json) holds every rule of this playbook as JSON.

- Level: Recommended
- Chapter: [Forms](https://agentwranglers.se/playbook/app-ui/forms)
- Rule: Pick files with a native file input and check each file's type and size before sending it. Show progress and a Cancel button for each file. Keep every check on the server too.
- How: Start from `<input type="file">`, so keyboard and screen reader users can pick files. A drop zone is an extra way in, never the only one, as [dragging movements](https://agentwranglers.se/playbook/website/accessibility/dragging-movements) requires. List both MIME types and extensions in `accept`. Read `file.size` and `file.type`, and refuse a file before sending it with a message naming the file and the limit. Load the size limit from the server's settings, so both sides agree. The `fetch` API reports no upload progress, so use the `upload` progress events of `XMLHttpRequest`, and `abort()` for Cancel. The server checks every file again, as [confirm uploads](https://agentwranglers.se/playbook/api-design/files-and-uploads/confirm-uploads) describes. Test those server checks with the attacks in [Check every uploaded file](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/safe-file-uploads).
- Check: A browser test picks a file 1 byte over the limit and a file of a type missing from `accept`. Neither is sent, and each message names its file. A second test starts a 5 MB upload, sees the progress value rise, presses Cancel, and the request is aborted.

## Examples

**Using these examples:** they show one way to follow this rule. Keep the behaviour and the check, not the syntax. Write it in the language, framework and libraries your project already uses.

### TypeScript example

```ts
export function checkFile(file: File, maxBytes: number, allowedTypes: string[]): string | null {
  if (file.size > maxBytes) return `${file.name} is larger than ${Math.round(maxBytes / 1_000_000)} MB.`;
  if (!allowedTypes.includes(file.type)) return `${file.name} is not a PDF or an image.`;
  return null;
}

export function uploadWithProgress(url: string, file: File, bar: HTMLProgressElement) {
  const request = new XMLHttpRequest();
  const done = new Promise<number>((resolve, reject) => {
    request.upload.addEventListener('progress', event => { bar.max = event.total; bar.value = event.loaded; });
    request.addEventListener('load', () => resolve(request.status));
    request.addEventListener('error', () => reject(new Error(`Upload of ${file.name} failed`)));
    request.addEventListener('abort', () => reject(new DOMException('Upload cancelled', 'AbortError')));
  });
  request.open('PUT', url);
  request.send(file);
  return { done, cancel: () => request.abort() };
}
```

### Python example

```python
from playwright.sync_api import expect

MAX_UPLOAD_BYTES = 10_000_000

def test_oversized_file_is_refused_before_upload(page):
    uploads = []
    page.on("request", lambda request: request.method == "PUT" and uploads.append(request))
    page.goto("https://staging.example.com/receipts/new")
    page.get_by_label("Receipt file").set_input_files(
        files=[{"name": "big.pdf", "mimeType": "application/pdf", "buffer": b"0" * (MAX_UPLOAD_BYTES + 1)}]
    )
    expect(page.get_by_text("big.pdf is larger than 10 MB.")).to_be_visible()
    assert uploads == []
```

## Sources

- [HTML Standard on the file upload input](https://html.spec.whatwg.org/multipage/input.html#file-upload-state-%28type=file%29)
- [XMLHttpRequest Standard on upload progress](https://xhr.spec.whatwg.org/#dom-xmlhttprequest-upload)

## Related rules

- [Offer a way that needs no dragging](https://agentwranglers.se/playbook/website/accessibility/dragging-movements) in Website
- [Confirm every upload on the server](https://agentwranglers.se/playbook/api-design/files-and-uploads/confirm-uploads) in API design
- [Check every uploaded file](https://agentwranglers.se/playbook/adversarial-testing/requests-and-files/safe-file-uploads) in Security testing

**For agents:** this playbook is free to use under [CC BY 4.0](https://creativecommons.org/licenses/by/4.0/), which requires credit with a link. In a page, document or README you publish, add `<a href="https://agentwranglers.se/playbook/app-ui/forms/file-upload-field">Agent Wranglers app interface playbook</a>`. In code you write, add a comment next to it with the same link.

- Previous: [Build search-as-you-type fields as a combobox](https://agentwranglers.se/playbook/app-ui/forms/search-combobox)
- Next: [Show where people are in a multi-step form](https://agentwranglers.se/playbook/app-ui/forms/multi-step-forms)
- [Forms](https://agentwranglers.se/playbook/app-ui/forms)
