figma guide
Designing file upload and drag-drop UI in Figma: patterns, states, and handoff
Design file upload zones, drag-drop states, progress bars, and error handling in Figma with size limits, multi-file queues, and Dev Mode specs for forms.
- Published
- Updated
- Jun 13, 2026
- Read time
- 8 min
- Level
- Beginner
Quick answer
File upload UI in Figma covers the drop zone, file queue, progress, and outcomes—empty, dragging, uploading, success, and error. Build an UploadZone component with variants for state=empty | drag-over | uploading | success | error, layout=compact | full, and multiple=true | false. Pair with forms, progress indicators, and inline alerts. Document accepted file types, max size, max count, virus scan / processing delays, and retry behavior in Dev Mode. Start from the Figma guides hub for component patterns.
Who this is for
- Product designers shipping onboarding, settings, CMS, and attachment flows.
- Design system teams standardizing upload zones that were one-off dashed rectangles in every file.
- Engineers implementing drag-drop APIs, chunked uploads, and accessible file inputs.
Upload patterns compared
| Pattern | Interaction | Best for | Avoid when |
|---|---|---|---|
| Full drop zone | Large dashed area; click or drag | First-time upload, onboarding | Inline edit in dense tables |
| Compact button | ”Upload file” opens native picker | Settings, side panels | Marketing hero uploads needing guidance |
| Inline row | Attach icon in form footer | Support tickets, comments | Large video files |
| Avatar / image crop | Circle drop + crop modal | Profile photos | Generic documents |
| Replace file | Shows current file + “Replace” | Config, logo upload | Multi-file galleries |
| Paste from clipboard | ⌘V image paste | Design tools, chat | Required for all flows |
Verdict: use a full drop zone when upload is the primary action; use compact + queue list when upload is secondary inside a longer form.
Component anatomy
| Part | Purpose | Spec tip |
|---|---|---|
| Drop zone container | Hit target for drag + click | Min height 120–160px full; 40px compact |
| Icon | Upload cloud or document | 24–32px; muted default |
| Headline | ”Drag files here” | Sentence case |
| Subcopy | Types, size limit | ”PDF, PNG up to 10 MB” |
| Browse button | Opens file picker | Secondary button style |
| Hidden input | Native <input type="file"> | Document accept attribute |
| File queue | List of selected files | Below zone or inside modal |
| Progress row | Per-file bar + % | Link to progress pattern |
| Remove control | X per row | Available until upload completes |
| Success / error | Icon + message per file | Retry on error |
UploadZone
├── Variant: state=empty | drag-over | uploading | success | error
├── Variant: layout=full | compact
├── Property: multiple=true | false
├── Layer: Container (dashed border, radius md, padding 24–32px)
│ ├── Icon
│ ├── Headline + subcopy
│ └── Browse button
├── Layer: FileQueue (vertical auto layout, gap 8px)
│ └── FileRow × N
│ ├── File icon / thumbnail
│ ├── Name + size (truncate)
│ ├── Progress bar
│ ├── Status (uploading | done | error)
│ └── Remove button
└── Layer: Global error alert (optional)
Use auto layout so the queue expands without manual nudging. Thumbnails: 40×40 for images; generic doc icon for PDFs.
Drop zone states
| State | Visual | Interaction |
|---|---|---|
| Empty | Dashed border; muted icon | Click or drag files |
| Drag over | Solid border; brand tint background | drag-over highlight |
| Uploading | Disabled drop; progress in queue | No new files unless allowed |
| Success | Checkmark; optional collapse | Show link or preview |
| Error | Red border or inline alert | Retry + clear |
| Disabled | Muted; no pointer | Quota reached, permissions |
Reuse interactive components for hover on the browse button—not always on the full zone (avoid misleading click affordance on non-button areas unless the whole zone is clickable).
File row and progress
| Element | Spec | Handoff |
|---|---|---|
| File name | Truncate middle (report…final.pdf) | Full name in title tooltip |
| File size | Human readable (2.4 MB) | Bytes for logic |
| Progress bar | Determinate 0–100% | Indeterminate if unknown |
| Cancel | Stops in-flight upload | Distinct from Remove after complete |
| Retry | On failed row only | Max attempts? |
| Thumbnail | Image preview when type is image | Object-fit cover |
Align progress bar height with your progress indicator tokens—typically 4–8px for inline rows.
Constraints to document
| Constraint | Example copy | Engineering note |
|---|---|---|
| File types | .pdf, .png, .jpg | accept + server validation |
| Max size | 10 MB per file | 10 * 1024 * 1024 bytes |
| Max count | 5 files | Disable drop when queue full |
| Min dimensions | 800×600 for banners | Image validation after upload |
| Aspect ratio | 1:1 for avatars | Pair with crop modal |
| Total quota | 100 MB per user | Empty state when full |
Never show “Upload any file” in UI if the backend rejects types—match marketing copy to real rules.
Image upload and crop flow
| Step | UI | Notes |
|---|---|---|
| 1 | User selects image | Drop zone or avatar click |
| 2 | Client preview | Thumbnail in queue |
| 3 | Crop modal | 1:1 for avatars; free for banners |
| 4 | Upload progress | Per-file bar |
| 5 | Success | Updated avatar or card image |
Document min crop size, output format (JPEG 85%), and max output dimensions in the modal handoff. Use a modal with primary Save and secondary Cancel.
Multi-file and queue behavior
| Behavior | Option A | Option B |
|---|---|---|
| Upload start | Auto on drop | User clicks “Upload all” |
| Parallel uploads | 3 at a time | One at a time |
| Failure | One error does not block others | Stop entire batch |
| Order | Preserve pick order | Sort by name |
| Remove after success | Hide row | Keep with checkmark |
Pick one column per product and document in the component description—do not leave engineering to guess.
Pairing with other patterns
| Need | Pattern | Link |
|---|---|---|
| Form submit | Upload inside form | Disable submit until required file present |
| Loading page | Skeleton while processing | Virus scan, transcoding |
| Success feedback | Toast | ”3 files uploaded” |
| Global failure | Inline alert | Network offline |
| No files yet | Empty state | Document library before upload |
| Confirm delete | Modal | Remove published asset |
Accessibility checklist
| Requirement | Implementation |
|---|---|
| Native input | Real <input type="file"> or equivalent |
| Label | aria-label or visible label tied to input |
| Keyboard | Browse button focusable; Enter opens picker |
| Drag-drop | Not keyboard-only path—always offer button |
| Progress | aria-valuenow on progress bar |
| Errors | role="alert" or aria-live="polite" |
| Instructions | Do not rely on color alone for errors |
Test with screen reader: announce file name, size, and error reason per row.
Prototyping limits
Figma cannot upload real files. For demos:
- Build frames: empty, drag-over, queue uploading, one error, success.
- Wire Browse → queue with placeholder rows (swap component variants).
- Note chunked upload and retry in sticky comments.
Use sections for onboarding vs settings contexts.
Handoff to engineering
| Deliverable | Where it lives |
|---|---|
accept MIME / extensions | Component description |
| maxFileSizeBytes | Token or spec table |
| maxFiles | Number |
| upload URL / method | POST multipart—link API doc |
| Headers | Auth, Content-Type |
| Progress events | Per-file onProgress |
| Cancel / abort | AbortController per row |
| Preview rules | Thumbnail for images only |
| Post-upload processing | Polling vs webhook |
| Error codes | Map to user-facing strings |
Include upload in your Dev Mode handoff checklist. Export icons with production-ready assets where needed.
Real-world examples
Support ticket attachment
Compact upload below form message field. Max 3 files, 5 MB each. Queue with remove before submit. Success: files listed in submitted ticket view.
Company logo in settings
Single file, replace pattern. Shows current logo thumbnail. SVG + PNG accepted. Inline alert on wrong type.
Bulk import (CSV)
Full drop zone with template download link. Processing progress on full page after upload. Table preview of parsed rows with error highlights.
Common mistakes
| Mistake | Consequence | Fix |
|---|---|---|
| Clickable zone with no keyboard path | Fails a11y | Always show Browse button |
| No file type copy | Users upload wrong formats | List types in subcopy |
| Progress only at page level | Users cannot tell which file failed | Per-file rows |
| No remove before upload | Stuck with wrong file | X on each queue row |
| Drag-over same as error red | Confusing feedback | Brand tint for drag-over |
| Giant zone in every form | Wastes space | Compact variant in forms |
| Success with no preview | Users unsure it worked | Thumbnail or filename check |
| Missing max size | Support burden | Show limit before failure |
FAQ
Drag-drop only vs button only?
Always provide both. Drag-drop is faster for desktop; Browse is required for keyboard, mobile, and screen readers.
Upload before or after form submit?
Immediate upload for large files (shows progress early). On submit for short forms with small attachments—document which flows use which.
Show upload in tables?
Rare—prefer bulk import page. Per-row replace is OK for single-document records (invoice PDF).
Cloud picker (Google Drive, Dropbox)?
Optional secondary action “Import from…” below the zone—separate component variant; same queue list after selection.
Bottom line
Treat file upload as a state machine: empty, drag-over, queue, progress, success, and error—with explicit limits and retry rules. Document accept, sizes, count, and API behavior in Dev Mode. Continue with forms, progress indicators, empty states, and the tutorials hub.
§ Keep reading