Upload
A drop zone and file list that manages client-side file selection, per-file XHR uploads, and aggregate state transitions with a built-in live region for assistive technology.
Features
- File picking
- Format filtering
- Per-file state
- Aggregate state
- Live announcements
- List templating
- Inline feedback
- Image previews
Installation
npm install @ariaui/upload
Examples
Drop zone and file list with per-file upload progress, aggregate status, and a polite live region for assistive technology.
Upload
A headless, accessible upload example.
No preview output yet.
Manual submit
A manual upload queue that waits for Upload.Submit before sending files.
No preview output yet.
Success
A successful upload flow that shows the uploaded state in the file row.
No preview output yet.
Anatomy
import * as Upload from "@ariaui/upload";
export default function Example() {
return (
<Upload.Root>
<Upload.Selector />
<Upload.List>
{(files) => (
<>
{files.map(() => (
<Upload.Item />
))}
{files.length > 0 && <Upload.Clear />}
</>
)}
</Upload.List>
</Upload.Root>
);
}
API Reference
Root
State owner for a complete upload flow. Tracks the selected files, object URLs, and the aggregate file state (`EMPTY` → `PROCESSED` → `UPLOADING` → `UPLOADED`). Renders a `<div>` and forwards all div props. Also renders a visually hidden `role="status"` live region that announces state transitions to assistive technology.
| Prop | Type | Default |
|---|---|---|
format | string[] | [] |
| Attribute | Values |
|---|---|
| data-state | aggregate file state: `'EMPTY' | 'PROCESSED' | 'UPLOADING' | 'UPLOADED'` (announced via live region) |
Selector
Drop zone and file picker trigger. Renders a `<div role="button">` with a hidden `<input type="file" multiple>` inside. Clicking or pressing `Enter` opens the native picker; dragging files over the element accepts them on drop.
| Prop | Type | Default |
|---|---|---|
id | string | — |
isDisabled | boolean | false |
| Attribute | Values |
|---|---|
| role | 'button' |
| tabIndex | 0 when enabled, -1 when `isDisabled` is true |
List
Owns the selected-file array for this list and passes it to a child render function. Map that array to `Upload.Item file={file}` rows, then render actions such as `Upload.Clear` or `Upload.Submit` from the same function.
| Prop | Type | Default |
|---|---|---|
url* | string | — |
method | 'POST' | 'GET' | 'PATCH' | 'DELETE' | 'POST' |
onError | (payload: UploadErrorPayload) => void | — |
onSuccess | (payload: UploadSuccessPayload) => void | — |
Item
Renders a single file row and owns the per-file XHR upload lifecycle. Compose it with file parts such as `Upload.FileName`, `Upload.FileStatus`, and `Upload.FileRemove`. Renders a `<div>` and forwards all div props. When the file finishes, `feedback.success` or `feedback.error` is rendered inline.
| Prop | Type | Default |
|---|---|---|
file* | UploadListFile | — |
feedback | { error?: React.ReactNode; success?: React.ReactNode } | — |
| Attribute | Values |
|---|---|
| data-state | 'processed' | 'uploading' | 'uploaded' | 'error' | 'abort' |
| data-progress | Current upload progress, 0–100 |
Actions
Prebuilt action parts for common upload controls. They keep upload state operations inside the primitive while still accepting normal button props.
| Prop | Type | Default |
|---|---|---|
AutoSubmit | { disabled?: boolean } | — |
Clear | button props | — |
Submit | button props | — |
File parts
Prebuilt row parts that render the current `Upload.Item` file metadata and controls without exposing upload state hooks in examples.
| Prop | Type | Default |
|---|---|---|
FileName | span props | — |
FileSize | span props | — |
FileExtension | span props + { fallback?: string; maxLength?: number } | — |
FileStatus | span props + { labels?; uploadingLabel? } | — |
FileProgress | div props + { indicatorClassName?: string; indicatorStyle?: CSSProperties } | — |
FileRemove | button props | — |
| Attribute | Values |
|---|---|
| data-state | 'processed' | 'uploading' | 'uploaded' | 'error' | 'abort' |
| data-value | Current upload progress on status/progress parts |
Keyboard
| Shortcut | Action |
|---|---|
| Tab | Move focus into and out of the selector and each focusable element inside the list. |
| Enter | While focus is on `Selector`: opens the native file picker. Equivalent to clicking the drop zone. |
| Space | Default native button behavior on `Selector` — activates the hidden file input. |
Accessibility
Upload follows standard form-control and live-region practices:
Rootrenders a visually hiddenrole="status"region witharia-live="polite"andaria-atomic="true". Each aggregate state transition (EMPTY→PROCESSED→UPLOADING→UPLOADED) is announced automatically.Selectorisrole="button"with a managedtabIndex(0 when enabled, -1 whenisDisabled).Enteropens the native file picker, matching the behavior of clicking the drop zone.- The hidden
<input type="file">insideSelectorships witharia-label="Upload files"andmultiple, so screen readers announce it as a file picker even though it is visually hidden. - Drag-and-drop handlers suppress the default browser behavior but leave the click + keyboard path untouched — users who cannot drag still have a full path to upload.
- Each file row should include an accessible name for its remove control.
Upload.FileRemovedefaults its label toRemove {fileName}. - When using
feedback.success/feedback.error, keep the content short and descriptive — it renders inline next to the file row, so screen readers encounter it in reading order. - Use
Upload.Submitfor a manual trigger orUpload.AutoSubmitwhen selected files should begin uploading immediately.