Skip to content
Development
Skill

/web-files-file-upload-patterns

File upload patterns - drag-drop dropzones, chunked and resumable uploads, presigned URL flows, file validation (MIME type, magic bytes), progress tracking, accessibility (ARIA)

From plugin
agents-inc-skills
24200 skills
Install
$ npx -y skills add agents-inc/skills --skill web-files-file-upload-patterns --agent claude-code

How it fires

How this skill gets triggered: by you, by Claude, or both.

  • Fires itselfAuto-invocation. Claude auto-loads it when your prompt matches the work.Auto-invocation is when the right skill fires by itself at the right moment, driven by a FLOW.md router and a hook, instead of you invoking it by name. It is the difference between a skill being installed and a skill actually getting used.Read the full definition →
  • You can call itInvoke it directly when you want it.
  • Slash command/web-files-file-upload-patterns

Context preview

The summary Claude sees to decide when to auto-load this skill.

File upload patterns - drag-drop dropzones, chunked and resumable uploads, presigned URL flows, file validation (MIME type, magic bytes), progress tracking, accessibility (ARIA)

SKILL.md

web-files-file-upload-patterns.SKILL.md
name: web-files-file-upload-patterns
description: File upload patterns - drag-drop dropzones, chunked and resumable uploads, presigned URL flows, file validation (MIME type, magic bytes), progress tracking, accessibility (ARIA)

File Upload Patterns

> **Quick Guide:** A dropzone is a keyboard-operable button wrapping a hidden file input, with drag > as an enhancement. Validate for the user's benefit on the client — extension, MIME type, then the > file's own magic bytes — and again on the server, because none of the client checks are security. > Progress needs `XMLHttpRequest`; `fetch` has no upload progress event. Past roughly 100MB, chunk > the file so a failure costs one chunk. Large files go straight to storage on a presigned URL the > server issues, so no request body is ever proxied.

**Detailed Resources:**

  • [examples/core.md](examples/core.md) — file input, dropzone, file list state and rendering, the assembled component
  • [examples/validation.md](examples/validation.md) — rule-based validator, magic-byte detection, dimension checks, a validation hook
  • [examples/progress.md](examples/progress.md) — XHR progress with speed and ETA, progress bar, formatters, concurrent uploads
  • [examples/preview.md](examples/preview.md) — a preview thumbnail for a selected file, with cleanup
  • [examples/presigned-upload.md](examples/presigned-upload.md) — PUT and POST-policy uploads, the server contract, multipart parts, the whole flow as a hook
  • [examples/resumable.md](examples/resumable.md) — chunked uploader with retry, resume across a reload, a tus client, the tus server contract
  • [examples/accessibility.md](examples/accessibility.md) — announcing selection and progress, focus return after the file dialog
  • [reference.md](reference.md) — method selection by size, expiry guidance, validation order, CORS, review checklist

---

Which path applies

The destination decides almost everything else.

  • **The file goes to your own endpoint** — one `POST` with `FormData`, progress from XHR, and a size

cap the server can enforce. [examples/core.md](examples/core.md) and [examples/progress.md](examples/progress.md) are the whole of it.

  • **The file goes to object storage** — the server issues a presigned URL and the browser uploads to

it directly, so no bytes pass through your application. [examples/presigned-upload.md](examples/presigned-upload.md).

  • **The file is large enough that a failure hurts** — split it, upload the parts with a concurrency

limit, and record which parts landed so a retry resumes. [examples/resumable.md](examples/resumable.md).

---

<critical_requirements>

Before writing upload code

**Validate on the server as well as in the browser.** Client validation exists to tell the user quickly what will be rejected; anyone can skip it entirely, so it settles nothing about safety.

**Read the file's first bytes when the type matters.** Extensions and MIME types are both supplied by whoever made the file, and a renamed executable passes every check that trusts them.

**Revoke every object URL you create.** A preview holds the whole file in memory until `URL.revokeObjectURL()` runs, so a user who changes their mind three times leaks three files.

**Make the dropzone reachable from the keyboard.** `role="button"`, `tabIndex={0}` and an Enter/Space handler that opens the file dialog, with drag layered on top — mobile has no drag at all, so the click path is the real one.

**Have the server issue a short-lived presigned URL rather than proxying the body.** The upload then costs your application nothing, and no storage credential is ever in reach of the browser.

</critical_requirements>

---

**Auto-detection:** dropzone, dataTransfer.files, dragenter, dragleave, dragover, input type="file", event.target.files, accept attribute, xhr.upload.addEventListener, lengthComputable, presigned URL, uploadUrl, multipart upload, UploadPart, ETag, chunked upload, file.slice, Content-Range, resumable upload, tus, Tus-Resumable, Upload-Offset, magic bytes, file signature, FormData append file

**Applies to:**

  • Selecting files by click, keyboard or drag
  • Validating type, size and dimensions before anything is sent
  • Reporting progress, speed and remaining time, and cancelling
  • Uploading straight to storage on a URL the server signed
  • Splitting a large file into chunks and resuming an interrupted upload
  • Announcing selection, progress and failure to a screen reader

**Handled elsewhere:**

  • Receiving, scanning and storing the bytes once they arrive
  • Resizing, cropping or converting an image before it is sent — this skill sends the `File` it is

given

  • Where the stored object lives, how it is served, and what its URL looks like
  • Streaming playback of media that was uploaded

---

<philosophy>

Philosophy

An upload is three independent problems that get conflated: choosing a file, checking it, and moving its bytes. Keeping them separate is what makes any of them replaceable.

The checking half has a rule that never bends. **Client validation is a user-experience feature, and the server's is the only one that is a control.** Everything the browser knows about a file — its name, its extension, its `type` — came from the file itself. Reading magic bytes raises the bar but does not change the category: it is still a check the client can be made to skip.

The moving half scales by a different axis: not how many files, but how long a single request is open. A short request can fail and be retried whole. A long one accumulates the probability of a dropped connection until retrying whole is unacceptable, and that is the point at which chunking starts paying for its complexity — not at a particular byte count.

</philosophy>

---

<patterns>

Core patterns

Pattern 1: Dropzone

Count drag events rather than tracking a boolean. `dragenter` and `dragleave` fire for every nested element, so a boolean flickers off the moment the pointer crosses a child.

Read more
Ships withagents-inc-skills

The official skills marketplace for Agents Inc. 150+ skills covering everything from React and Prisma to Redis, ElevenLabs, and infrastructure tooling. Pick the skills that match your stack and install them via Claude Code. Need more control?

Get the whole plugin

Other skills on agents-inc-skills.