The engine, the lifecycle and the layers on top.
Layers
Browser
Your appcopied by the shadcn CLI, you own it
components/ui/upload.tsxcomponents/file-upload.tsx…@uploadcn/reactnpm package
Hooks and headless, accessible primitives@uploadcn/corenpm package · no React, no framework
Engine · queue · retries · validation · adapters · imagebytes go direct
Server (optional)
@uploadcn/serverSigns uploads, checks auth and limits. Any runtime.
StorageS3R2MinIOCloudinaryDiskYour APIBrowser only
The engine (createUploader) owns all state. React bindings subscribe to it with
useSyncExternalStore, so list items re-render only when their own item changes.
Lifecycle
- Select
- Validate
- Transform
- Queueconcurrency
- Uploadprogress · retry
- Process
- Scan
- Complete
- UploadPaused, pause, offline, resume continues from the last part
- UploadQueue, automatic retry with backoff on network and 5xx errors
- Any stepCancelled, cancel aborts the request and cleans up multipart state
- Select: picker, drop (folders expanded), paste, or
uploader.add(files). - Validate: type, size, count, duplicates, image dimensions, media duration and
your own (async) validators. Failing files become
rejectedwith readable issues. - Transform: optional
transform(file): crop, compress, convert. - Queue:
autoUploadqueues immediately; otherwise items wait asidleuntilstart(). At mostconcurrencyuploads run at once. - Upload: the adapter moves the bytes and reports progress. Retryable failures are retried with exponential backoff; resumable adapters can pause and resume.
- Process / Scan: optional
process(item)reflects server-side work. - Complete:
success, orerror/rejectedwith a reason.
States
| Status | Meaning |
|---|---|
validating | Running validators and transforms |
idle | Ready, waiting for start() |
queued | Waiting for a free slot (or for a retry delay, see retryAt) |
uploading | Bytes are moving |
paused | Paused by the user, by a network loss (pauseReason: "offline") or restored after a reload |
processing | Stored; the server is working on it |
scanning | Stored; a malware scan is running on the server |
success | Done |
error | Failed; can be retried |
cancelled | Cancelled by the user; can be retried |
rejected | Refused by validation or by the server; final |
Errors
Every failure is an UploadError with a code (network, timeout, http,
offline, validation, rejected, …), an optional HTTP status, and a retryable
flag. Timeouts, 408, 425, 429 and 5xx are retried; other 4xx are not.
Retry-After headers are honored.
Events
uploader.on("success", ({ item }) => {})
uploader.on("error", ({ item, error }) => {})
uploader.on("retry", ({ item, error, delay }) => {})
uploader.on("complete", ({ items }) => {}) // queue drainedAll events: add, reject, start, progress, pause, resume, retry,
success, error, cancel, remove, statuschange, complete, online.