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.

Preview

No preview output yet.

live.tsxReady

Manual submit

A manual upload queue that waits for Upload.Submit before sending files.

Preview

No preview output yet.

live.tsxReady

Success

A successful upload flow that shows the uploaded state in the file row.

Preview

No preview output yet.

live.tsxReady

    Anatomy

    tsx
    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.

    PropTypeDefault
    format
    string[][]
    AttributeValues
    data-stateaggregate 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.

    PropTypeDefault
    id
    string
    isDisabled
    booleanfalse
    AttributeValues
    role'button'
    tabIndex0 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.

    PropTypeDefault
    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.

    PropTypeDefault
    file*
    UploadListFile
    feedback
    { error?: React.ReactNode; success?: React.ReactNode }
    AttributeValues
    data-state'processed' | 'uploading' | 'uploaded' | 'error' | 'abort'
    data-progressCurrent upload progress, 0100

    Actions

    Prebuilt action parts for common upload controls. They keep upload state operations inside the primitive while still accepting normal button props.

    PropTypeDefault
    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.

    PropTypeDefault
    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
    AttributeValues
    data-state'processed' | 'uploading' | 'uploaded' | 'error' | 'abort'
    data-valueCurrent upload progress on status/progress parts

    Keyboard

    ShortcutAction
    TabMove focus into and out of the selector and each focusable element inside the list.
    EnterWhile focus is on `Selector`: opens the native file picker. Equivalent to clicking the drop zone.
    SpaceDefault native button behavior on `Selector` — activates the hidden file input.

    Accessibility

    Upload follows standard form-control and live-region practices:

    • Root renders a visually hidden role="status" region with aria-live="polite" and aria-atomic="true". Each aggregate state transition (EMPTYPROCESSEDUPLOADINGUPLOADED) is announced automatically.
    • Selector is role="button" with a managed tabIndex (0 when enabled, -1 when isDisabled). Enter opens the native file picker, matching the behavior of clicking the drop zone.
    • The hidden <input type="file"> inside Selector ships with aria-label="Upload files" and multiple, 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.FileRemove defaults its label to Remove {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.Submit for a manual trigger or Upload.AutoSubmit when selected files should begin uploading immediately.
    Previous
    Treeview