Skip to content

/web-files-file-upload-patterns

File upload patterns - drag-drop dropzones, chunked/resumable uploads, S3 presigned URLs, file validation (MIME type, magic bytes), progress tracking, image preview, accessibility (ARIA)

shell
$ 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.
  • You can call itInvoke it directly when you want it.
  • Slash command/web-files-file-upload-patterns
How auto-invocation works

Context preview

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

File upload patterns - drag-drop dropzones, chunked/resumable uploads, S3 presigned URLs, file validation (MIME type, magic bytes), progress tracking, image preview, 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/resumable uploads, S3 presigned URLs, file validation (MIME type, magic bytes), progress tracking, image preview, accessibility (ARIA)

File Upload Patterns

> **Quick Guide:** Use drag-and-drop dropzones with fallback file inputs for uploads. Validate files client-side (MIME + magic bytes) AND server-side. For files >5MB use chunked uploads with progress tracking. Upload directly to S3 using presigned URLs to avoid server bottlenecks. Always implement proper accessibility with keyboard support and ARIA announcements. Use XHR (not fetch) for upload progress events.

---

<critical_requirements>

CRITICAL: Before Using This Skill

> **All code must follow project conventions in CLAUDE.md** (kebab-case, named exports, import ordering, `import type`, named constants)

**(You MUST validate files BOTH client-side AND server-side - client validation is UX only, not security)**

**(You MUST use magic bytes detection for security-critical uploads - MIME types and extensions can be spoofed)**

**(You MUST cleanup object URLs with `URL.revokeObjectURL()` to prevent memory leaks)**

**(You MUST provide keyboard support for dropzones - Enter/Space to open file dialog)**

**(You MUST use presigned URLs for cloud storage uploads - never proxy large files through your server)**

</critical_requirements>

---

**Auto-detection:** file upload, dropzone, drag-drop, drag and drop upload, useDropzone, TUS protocol, presigned URL, multipart upload, file validation, magic bytes, MIME type, progress indicator, upload progress, XHR upload, S3 upload, file input, aria-label upload, chunked upload, resumable upload

**When to use:**

  • Building file upload interfaces (single or multi-file)
  • Implementing drag-and-drop upload areas
  • Uploading to cloud storage directly from browser
  • Validating file types before upload
  • Showing upload progress with speed/ETA
  • Handling large file uploads with chunking/resumable support
  • Creating accessible file upload components

**When NOT to use:**

  • Server-side file processing (use backend skills)
  • File storage architecture (use infrastructure skills)
  • Video/audio streaming (use media handling skills)

---

**Detailed Resources:**

  • [examples/core.md](examples/core.md) - Dropzone, file list, combined upload component
  • [examples/validation.md](examples/validation.md) - MIME type, magic bytes, dimension validation
  • [examples/progress.md](examples/progress.md) - Progress tracking, speed, abort, multi-file
  • [examples/preview.md](examples/preview.md) - Image preview, thumbnails, EXIF orientation
  • [examples/s3-upload.md](examples/s3-upload.md) - Presigned URLs, multipart uploads
  • [examples/resumable.md](examples/resumable.md) - Chunked uploads, TUS protocol
  • [examples/accessibility.md](examples/accessibility.md) - ARIA patterns, keyboard, announcements
  • [reference.md](reference.md) - Decision frameworks, anti-patterns, checklists

---

<philosophy>

Philosophy

File uploads are deceptively complex. A simple file input works for basic cases, but production apps need validation, progress feedback, error handling, and accessibility. The key insight is that **client-side validation is for UX, not security** - always validate on the server too.

**Core Principles:**

1. **Defense in depth** - Validate extension + MIME type + magic bytes + server-side 2. **Progressive enhancement** - Drag-drop enhances but doesn't replace click-to-browse 3. **Direct uploads** - Use presigned URLs to upload to cloud storage directly, not through your server 4. **Chunked for reliability** - Large files need chunking for resumability and progress 5. **Accessibility first** - Keyboard navigation and screen reader support from day one

**File Size Strategy:**

| File Size | Upload Method | Progress UI | Storage Pattern | | --------- | ------------------- | ---------------------- | -------------------- | | < 5MB | Single request | Spinner or bar | Direct presigned PUT | | 5-50MB | Single request | Progress bar | Presigned PUT | | 50MB-5GB | Chunked | Progress + ETA | Multipart presigned | | > 5GB | Chunked + resumable | Progress + ETA + pause | Multipart required |

</philosophy>

---

<patterns>

Core Patterns

Pattern 1: Drag-and-Drop Dropzone

Build a dropzone with drag-and-drop and fallback file input. Key elements:

  • Track drag state with a counter ref (not boolean) to handle nested element events
  • `role="button"` + `tabIndex={0}` + Enter/Space key handlers for keyboard access
  • Hidden `<input type="file">` triggered by click/keyboard
  • Type validation via `accept` attribute plus runtime checking
  • Reset `event.target.value = ''` after selection to allow re-selecting same file
// Key structure - full implementation in examples/core.md
export function FileDropzone({ onFilesSelected, accept, multiple, disabled }: FileDropzoneProps) {
  const inputRef = useRef<HTMLInputElement>(null);
  const dragCounterRef = useRef(0); // Handles nested element drag events

  return (
    <div
      onDrop={handleDrop}
      onDragEnter={() => { dragCounterRef.current++; setState('drag-over'); }}
      onDragLeave={() => { dragCounterRef.current--; if (dragCounterRef.current === 0) setState('idle'); }}
      onClick={() => inputRef.current?.click()}
      onKeyDown={(e) => { if (e.key === 'Enter' || e.key === ' ') inputRef.current?.click(); }}
      role="button"
      tabIndex={disabled ? -1 : 0}
      aria-label="File upload area. Click or drag files to upload."
    >
      <input ref={inputRef} type="file" hidden aria-hidden="true" tabIndex={-1} />
    </div>
  );
}

See [examples/core.md](examples/core.md) for full implementation.

---

Pattern 2: File List Management Hook

Track multiple files with status, progress, and preview URLs:

// use-file-list.ts -
Read more
Read it on GitHub ↗

Showing the first part of this file.

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, auto-invoked