How it works

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 · image
bytes go direct
Server (optional)
@uploadcn/serverSigns uploads, checks auth and limits. Any runtime.
Storage
S3R2MinIOCloudinaryDiskYour 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

  1. Select
  2. Validate
  3. Transform
  4. Queueconcurrency
  5. Uploadprogress · retry
  6. Process
  7. Scan
  8. 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
  1. Select: picker, drop (folders expanded), paste, or uploader.add(files).
  2. Validate: type, size, count, duplicates, image dimensions, media duration and your own (async) validators. Failing files become rejected with readable issues.
  3. Transform: optional transform(file): crop, compress, convert.
  4. Queue: autoUpload queues immediately; otherwise items wait as idle until start(). At most concurrency uploads run at once.
  5. Upload: the adapter moves the bytes and reports progress. Retryable failures are retried with exponential backoff; resumable adapters can pause and resume.
  6. Process / Scan: optional process(item) reflects server-side work.
  7. Complete: success, or error / rejected with a reason.

States

StatusMeaning
validatingRunning validators and transforms
idleReady, waiting for start()
queuedWaiting for a free slot (or for a retry delay, see retryAt)
uploadingBytes are moving
pausedPaused by the user, by a network loss (pauseReason: "offline") or restored after a reload
processingStored; the server is working on it
scanningStored; a malware scan is running on the server
successDone
errorFailed; can be retried
cancelledCancelled by the user; can be retried
rejectedRefused 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 drained

All events: add, reject, start, progress, pause, resume, retry, success, error, cancel, remove, statuschange, complete, online.

On this page