Skip to content

File Upload

A drop zone and button for choosing files, with type, size and count limits and a list of the chosen files you can remove from. The component picks the files; you upload them.

Inputv0.1.0experimentalWCAG 2.2 AAView spec
Attachments
Drag files here, or

Up to 5 files, 5 MB each.

0 ready to upload

import { useState } from "react";
import { FileUpload } from "@rdloom/react";

export default function FileUploadBasicExample() {
  const [files, setFiles] = useState<File[]>([]);
  return (
    <div className="w-[26rem] max-w-full">
      <FileUpload label="Attachments" description="Up to 5 files, 5 MB each." maxFiles={5} maxSize={5 * 1024 * 1024} onChange={setFiles} />
      <p className="mt-2 text-xs text-[var(--rd-color-text-muted)]">{files.length} ready to upload</p>
    </div>
  );
}

Installation

npx rdloom add file-upload

Copies the source into src/components/rdloom. Edit it freely: rdloom upgrade merges later versions into your changes. It needs react-aria-components; add --install to install them.

Use another registry client
npx shadcn@latest add https://rdloom.vimalbhatt.com/r/file-upload.json

Works, but without upgrade tracking.

Usage

import { FileUpload } from "@rdloom/react";

<div className="w-[26rem] max-w-full">
  <FileUpload label="Attachments" description="Up to 5 files, 5 MB each." maxFiles={5} maxSize={5 * 1024 * 1024} onChange={setFiles} />
  <p className="mt-2 text-xs text-[var(--rd-color-text-muted)]">{files.length} ready to upload</p>
</div>

Images

Photos
Drag files here, or

PNG or JPEG, up to 2 MB each.

import { FileUpload } from "@rdloom/react";

// accept takes MIME types, wildcards and extensions. Check the files on your server too.
export default function FileUploadImagesExample() {
  return (
    <div className="w-[26rem] max-w-full">
      <FileUpload label="Photos" description="PNG or JPEG, up to 2 MB each." accept={["image/png", "image/jpeg"]} maxSize={2 * 1024 * 1024} browseLabel="Choose photos" />
    </div>
  );
}

Single

Resume
Drag files here, or

A PDF, up to 10 MB.

import { FileUpload } from "@rdloom/react";

// With multiple off, choosing a new file replaces the old one.
export default function FileUploadSingleExample() {
  return (
    <div className="w-[26rem] max-w-full">
      <FileUpload label="Resume" description="A PDF, up to 10 MB." multiple={false} accept={[".pdf"]} maxSize={10 * 1024 * 1024} />
    </div>
  );
}

API Reference

Defined by the spec. Components also accept the props of the React Aria component they wrap.

PropTypeDefault
labelrequired

What the files are for, e.g. "Attachments". It names the control.

stringnone
description

Help text, e.g. the limits.

stringnone
accept

Allowed types: MIME types like "image/png" or "image/*", or extensions like ".pdf". Others are rejected with a message.

readonly string[]none
multiple

Allow more than one file.

booleantrue
maxSize

Largest file in bytes. Bigger files are rejected.

numbernone
maxFiles

Most files allowed in total.

numbernone
value

Controlled list of chosen files.

readonly File[]none
defaultValue

Initial files when uncontrolled.

readonly File[]none
onChange

Called with the full list of files whenever it changes.

(files: File[]) => voidnone
onReject

Called with files that were refused, and why. The component also lists them.

(rejections: { file: File; reason: "type" | "size" | "count" }[]) => voidnone
browseLabel

Text of the button that opens the file picker.

string"Browse files"
isDisabled

Prevents choosing, dropping and removing.

booleanfalse

Accessibility

Role group, WCAG 2.2 AA. Tested with axe and keyboard tests; screen reader checks are in the audit checklist.

Keyboard

  • Tab reaches the Browse files button; Enter or Space opens the file picker
  • Tab reaches each Remove button; Enter or Space removes that file

Screen readers announce

  • The group is announced with its label and description
  • Adding files announces how many were added, e.g. "2 files added"
  • A rejected file is announced with its name and reason, e.g. "photo.tiff not added: file type not allowed"
  • Removing announces the file removed

What your code must do

  • Dragging is an extra: everything can be done with the Browse files button and the keyboard
  • The group is named by its label and described by the description
  • Each Remove button names its file (Remove report.pdf)
  • Adding, removing and rejecting files are announced in a polite live region
  • Rejected files and the reason are written out, not only coloured
  • The drag-over highlight is also a change of text, not only of color

Guidelines

Use it when

  • Attachments, imports, avatars and documents
  • When people may have the files on their desktop to drag in

Avoid it when

  • Uploading as a background process with progress: pair it with Progress and your own queue
  • Picking one value from a list

Don't

  • A drop zone with no button
  • Limits that are only discovered after upload
  • Trusting accept or maxSize alone: check files on the server too

Design tokens

The semantic tokens this component uses. Change them once and every component follows; see Design tokens.

  • --rd-color-surface-default
  • --rd-color-surface-subtle
  • --rd-color-surface-selected
  • --rd-color-border-default
  • --rd-color-border-strong
  • --rd-color-action-primary
  • --rd-color-text-default
  • --rd-color-text-muted
  • --rd-color-feedback-danger
  • --rd-color-focus-ring
  • --rd-radius-overlay
  • --rd-radius-control