Interface Lab
Back to knowledge
Frontend Implementation

File Upload State and Validation Contract

An uploader is not a dashed box. It is a state machine from selection, validation, upload, pause, failure, retry, and final file record; drag-and-drop is enhancement, not a replacement for accessible file selection and server validation.

Design Principle

File upload spans interaction, network, privacy, security, and accessibility. A healthy uploader defines file purpose, restrictions, API, states, and recovery before choosing react-dropzone, Uppy, FilePond, tus-js-client, or native input.

Large Theory

File upload is the boundary where users hand local or cloud-private content to the system. It looks like one control, but it includes selection entry, format restrictions, per-file queues, network transfer, security validation, privacy notes, status announcements, and final object records. Design should treat drag-and-drop as a fast path, a focusable browse/input as the baseline path, and server validation as the real security boundary.

Small Points

  1. Start with an upload brief: file purpose, accepted types, max size, max count, multi-file support, sensitivity, upload timing, resumability, and final remoteId.
  2. accept is only a browser picker hint. Client validation can guide users early, but the server must re-check MIME, extension, size, count, permission, virus scanning, and business rules.
  3. Per-file rows matter more than total progress: each file keeps name/extension, size, type, thumbnail or icon, state, progress, cancel, remove, retry, and error reason.
  4. Cover at least idle, drag-active, drag-reject, selected, validating, uploading, paused, success, partial-success, error, retrying, cancelled, and removed.
  5. Accessible announcements should be restrained. W3C ARIA25 uses a live region for upload progress status and notes that progressbar value changes are not automatically announced; real UI should announce meaningful stage changes instead of every 1% increment.
  6. Mobile usually has no drag. Provide a clear browse button, camera/file entry, safe-area behavior, long-filename wrapping, and recovery after the system picker returns.
  7. Implementation resources are layered: react-dropzone handles customizable selection/drag entry, FilePond handles a fuller component experience, Uppy handles complex queues/remote sources/plugins, and tus-js-client handles resumable upload protocol.

Design Judgment

The upload contract works when users can tell which files were selected, which were rejected, why something failed, whether cancel/retry is possible, where uploaded files went, and can complete the path with keyboard, screen reader, and mobile. A page that only says drop here or shows one spinner is still missing state evidence.

Implementation Notes

Create UploadFile and UploadContract: id, File, name, extension, size, type, previewUrl, status, progress, errorCode, errorMessage, remoteId, uploadEndpoint, validationSource, cancelToken, retryPolicy, scanStatus, and privacyNote. Preserve the selection path through native file input or mature libraries. Revoke object URLs on remove/unmount. For large files using tus or chunked protocols, design resume fingerprints, expiration, conflicts, and backend compatibility. QA should cover wrong type, oversized file, partial success, slow network, cancel/retry, screen-reader announcements, mobile picker, long names, failed thumbnails, and unmount cleanup.

Counterexamples/Risks

Using accept="image/*" and skipping server validation, making drag the only upload path, labeling every failure Upload failed, hiding file extensions with long names, providing no cancel while uploading, removing failed rows so retry is impossible, sending remote cloud-source files through third-party Companion without privacy disclosure, and leaking object URLs or requests after route changes.

Case Study

Carbon File Uploader separates button upload from drag-and-drop variants, shows upload process guidance, warns against multi-file uploads in modals, and expects recoverable error messages. Shopify Drop zone supports both drag-and-drop and click-to-browse and names images, documents, CSV imports, media management, and document collection as use cases. MDN's accept documentation states that it only hints file type and users may override it, so server validation remains necessary. Uppy, FilePond, and tus-js-client show that a full upload suite, an accessible multi-framework component, and a resumable upload protocol are different layers that agents should not collapse into one uploader choice.

Source Links

Carbon File uploader: https://carbondesignsystem.com/components/file-uploader/usage/ Shopify Drop zone: https://shopify.dev/docs/api/app-home/web-components/forms/drop-zone MDN accept attribute: https://developer.mozilla.org/en-US/docs/Web/HTML/Reference/Attributes/accept W3C ARIA25 upload progress: https://www.w3.org/WAI/WCAG21/Techniques/aria/ARIA25 react-dropzone: https://github.com/react-dropzone/react-dropzone Uppy: https://github.com/transloadit/uppy FilePond: https://github.com/pqina/filepond tus-js-client: https://github.com/tus/tus-js-client

Agent Directive

Before generating upload UI, output upload brief, state machine, client/server validation matrix, per-file rows, error recovery, aria-live policy, privacy notes, API contract, and cleanup; do not only draw a default dropzone.