File upload
● Stable · v0.1Choose one or more documents to attach, then watch each one upload, succeed, or fail — right where you picked it. A dropzone for the empty state, a clear row per file for everything after.
In context
Try it
On the web this opens your real file picker and simulates the upload. On a phone it asks the app to open the document picker instead.
Every state
A first-run owner needs to know, at a glance, whether a file is going up, went up, or failed — so each file carries its own status, not a single bar for the whole field.
Purpose
File upload attaches documents — a licence, a statement, a signed form. The dropzone keeps the empty state inviting and tappable; once files are chosen, each becomes a row showing its name, size, and progress so nothing uploads silently.
Anatomy
- Dropzone — a dashed, full-width tap target with an upload mark and a one-line prompt. It hides once a single-file field is filled, or when the
maxis reached. - File row — a type badge (PDF, JPG…), the file name, and a secondary line that becomes a progress bar while uploading or an error message if it fails.
- Row actions — a check on success, Retry on failure, and a remove (×) to take a file back off.
- Helper / error — quiet guidance (accepted types, size limit) that turns into a red message for a field-level validation error.
Behaviour
It's controlled: you own the files array and each file's status,
progress (0–1), and error. On web, tapping the dropzone calls onSelect
with the chosen files; on native it calls onRequestPick so you can open
expo-document-picker. onRemove and onRetry fire per row.
Code
import { FileUpload } from '@daytwo/components'const [files, setFiles] = useState([])<FileUploadlabel="Attach documents"helperText="PDF, JPG or PNG · up to 10 MB each"multipleaccept="image/*,.pdf"files={files}// Web: receives the picked filesonSelect={(picked) => addAndUpload(picked)}// Native: open expo-document-picker hereonRequestPick={pickWithExpo}onRemove={(id) => setFiles((f) => f.filter((x) => x.id !== id))}onRetry={(id) => retry(id)}/>
Each item in files is { id, name, size?, status?, progress?, error? } where
status is 'idle' | 'uploading' | 'success' | 'error'.
When to use
- To attach documents to a form — a licence, a receipt, a statement.
- When you need to show upload progress and let a failed file be retried.
- When more than one file may be attached (set
multiple, optionally cap withmax).
When NOT to use
- For photos, use Image upload — a thumbnail grid reads better than a filename.
- For a single quick value the owner could speak, consider Voice input.
- As an action button — the dropzone picks files; saving the form is a button.
Anti-patterns
A failed upload must say what went wrong — too big, wrong type, no connection — in words the owner can act on. "Error" tells them nothing.
Edge cases
max files are added