File uploads — pickers, drop zones, chunking, resumability
draftedconfidence: mediumthis tier holds 71% on the public scorecard (7/20 graded · 0 overturned) →
recommendation
react-dropzone for the input on web, plus your own transport (presigned PUT / plain POST); on React Native, expo-file-system uploading straight from the picked file URI. Two conditions change the web pick: large files on unreliable networks with a tus-capable backend pick tus-js-client; imports from third-party sources or a file-manager UI pick Uppy.
- web, ordinary attachments (avatars, documents, images) → react-dropzone (+ a presigned URL; no upload framework needed)
- large files over unreliable networks, where resumability is the requirement → tus-js-client (confirm the server speaks tus before committing)
- users import from Google Drive / OneDrive / a URL, or you need a file-manager UI → Uppy (its provider plugins are what nothing else here replaces)
- React app that wants upload queue, progress and retry as hooks without a UI kit → @rpldy/uploady (viable but unvetted; @rpldy/native-uploady on RN)
- React Native, ordinary uploads → expo-file-system (from the picked URI; an upload library would add a dependency for a POST)
- React Native, must keep uploading while backgrounded → react-native-background-upload (the only route, dormant since 2022 — adopting it means taking on its maintenance, or move the requirement server-side)
- the bytes are images/video you captured in-app → RB-E-MEDIA (owns the picker and capture side; this entry starts once you hold a file)
- upload endpoints accept files from untrusted users → server-side validation (type sniffing, size caps, virus scanning; client accept/maxSize is UX, not a control — see RB-E-SECURITY)
Options & tradeoffs
| option | tradeoff | evidence |
|---|---|---|
| react-dropzone | the drop-zone INPUT only: a hook for drag-and-drop plus file selection with accept/size validation that hands you File objects — you still write the transport. Largest deployed base and the cohort's most common pick (5 of 34 apps). 20.1.1 (2026-08-20), ~11.6M weekly (verified 2026-09-10 vs npm) | 16.4M/wk · ships in 5/34 |
| Uppy (@uppy/*) | batteries-included: Dashboard UI, provider plugins (Google Drive, OneDrive, URL import, webcam, image editor) and a resumable transport, from scoped packages. Earns its size only for third-party imports or a file-manager UI; overkill for 'attach a photo'. @uppy/core 6.0.1 (2026-09-07), ~1.1M weekly (verified vs npm) | 1.6M/wk · ships in 1/34 |
| tus-js-client | the RESUMABLE-PROTOCOL lane: tus uploads survive a dropped connection and continue where they stopped. Requires a tus-capable server (Supabase ships one), so it is a backend decision too. 4.3.1 (2025-01-16, verified 2026-09-24 vs npm), ~1.5M weekly (2026-09-10); a finished protocol client does not churn | 2.6M/wk · ships in 1/34 |
| @rpldy/uploady | React-idiomatic upload state (queue, progress, retry) through hooks/components rather than a UI kit — between react-dropzone's input-only scope and Uppy's dashboard; the RN variant is @rpldy/native-uploady. 1.13.0 (2025-11-26, verified vs npm), ten months quiet, no cohort adoption: viable but unvetted | 54k/wk |
| expo-file-system (RN, first-party) | on React Native the default: upload directly from a file URI with the platform's own networking, no extra dependency; pair it with the picker you already have (RB-E-MEDIA). Leaves background continuation uncovered. 57.0.6 (2026-09-09), versions with the Expo SDK | |
| react-native-background-upload | native background transfer sessions, so uploads continue while the app is backgrounded. DORMANT: 6.6.0 last published 2022-10-07, ~4.9k weekly (verified 2026-09-10 vs npm). Named because the capability has no maintained RN alternative, not because it is a safe dependency; see note: REACT NATIVE |
npm weekly downloads (from the corpus's last signals run): react-dropzone 16.4M · @uppy/core 1.6M · tus-js-client 2.6M · @rpldy/uploady 54k · react-native-background-upload 8k
Verified notes
The upload libraries solve three different problems, not three tiers of one: the INPUT (react-dropzone), third-party PROVIDERS plus a file-manager UI (Uppy), and RESUMABILITY over a tus-capable server (tus-js-client). Picking Uppy because it "does more" is the common mistake — its weight buys third-party providers, and if users upload from their own device you pay for a surface you never render. Equally, react-dropzone does NOT upload anything; teams reach for it expecting transport and then hand-roll the part that actually breaks. COHORT EVIDENCE (2026-09-10, verified vs the census cohort's package.json files): 7 of 34 apps carry a dedicated upload library, split by problem — react-dropzone in outline, twenty, plane, metabase and documenso (INPUT); @uppy/* in appsmith (PROVIDERS + file-manager UI); tus-js-client in supabase (RESUMABILITY, backed by its tus server). → The three lanes are a real selection axis. REACT NATIVE (2026-09-10, verified vs npm and the census cohort): the RN side is thin. None of the cohort's React Native apps carries an upload library; the maintained first-party path (expo-file-system) covers foreground uploads; the one library covering background continuation has been silent since 2022 at ~4.9k weekly downloads. → If background upload is a hard requirement, plan for a gap: review or fork that library, or move the requirement server-side. CROSS-REFERENCES: RB-E-MEDIA owns pickers and capture; RB-E-SECURITY owns server-side upload validation. HISTORY: 2026-09-24 CORRECTED: tus-js-client 4.3.1 was dated 2026-01-13, npm says 2025-01-16; @rpldy/uploady was called React-Native-idiomatic, but it is the React package and @rpldy/native-uploady is the RN one. 2026-09-10 GAP CLOSED: entry opened on cohort evidence (7/34 apps, three distinct lanes); Uppy 6.0 appearing twice in one harvest pass prompted the probe.
Canonical reading
Editorial annotations on why each piece matters — the articles themselves are the originals; read them there.
Worth reading even if you never install Uppy, because the @uppy/aws-s3 rewrite is a case study in the failure mode that makes direct-to-S3 uploads flaky. The old plugin exposed up to eight callbacks (getUploadParameters, signPart, createMultipartUpload, listParts, abortMultipartUpload, completeMultipartUpload and friends) where WHICH subset you needed depended on whether you were doing multipart, whether Companion was involved, and whether you signed on the client — with nothing telling you up front, so people wired plausible-looking combinations that quietly did not work. The team's own diagnosis is the transferable part: the problem was the SHAPE of the API, not any individual bug in it. The replacement is three mutually exclusive signing modes — sign on the client with SigV4, bring your own signer, or let a server sign — and you pick exactly one. Two consequences beyond Uppy: the plugin now speaks to any S3-compatible service (R2, MinIO, DigitalOcean Spaces) with no provider-specific code, and their proxy is no longer required in the data path. The reliability bugs it closed name the hard parts of resumable upload precisely — retries with backoff, credential expiry, offline detection, and pause/resume races during multipart — which is a good checklist against your own implementation if you hand-rolled one. They also moved from mocks to testing against a real MinIO instance in CI, which is how several of those were found.
Sources
- registry.npmjs.org/react-dropzone/latest
- registry.npmjs.org/@uppy%2Fcore/latest
- registry.npmjs.org/tus-js-client/latest
- registry.npmjs.org/react-native-background-upload/lat…
- registry.npmjs.org/expo-file-system/latest
- uppy.io/blog/uppy-6.0
- github.com/rpldy/react-uploady
Related in app-architecture: state · data · p2p · nav · meta-frameworks · forms · auth · networking · crossplatform · desktop