Skip to content

FileUpload

Drag-and-drop file picker with accept/size/count validation, four trigger variants, and a built-in list with image thumbnails or file-type badges.

  • stable
  • since v0.1.0
  • 10.7 kB
  • forms
  • upload

Wraps a native <input type="file">; the drop zone exposes role="button" with full keyboard activation (Enter/Space) and an aria-describedby hint when accept is set.

Preview
Open
tsx
import { FileUpload } from "@arshad-shah/cynosure-react";
<FileUpload onFilesChange={(files) => console.log(files)} />

The default render is FileUploadTrigger + FileUploadList — pass children to compose your own layout.

default (the main affordance), card, compact, and minimal change the trigger’s visual treatment.

Preview
Open
tsx

multiple enables multi-select and cumulative drops. maxCount caps the list size.

Preview
Open
tsx

Restrict by MIME type, extension, or wildcard via accept. Cap individual file bytes with maxSize; the onError callback fires for rejected files with a reason of "type", "size", or "count".

Preview
Open
tsx

Drop in just the trigger or just the list when you need a custom layout — both read from the FileUpload context.

Preview
Open
tsx
Preview
Open
tsx
PropTypeDefaultDescription
accept
string
MIME type / extension filter, e.g. `"image/*,.pdf"`.
maxSize
number
Maximum allowed file size in bytes. Files exceeding it call `onError({ reason: "size" })`.
maxCount
number
Hard cap on number of files; ignored when `multiple` is false.
multiple
boolean
false
Allow selecting more than one file.
disabled
boolean
false
Disables the trigger and clears focus.
value
File[]
Controlled list of selected files.
defaultValue
File[]
Uncontrolled initial files.
onFilesChange
((files: File[]) => void)
Fires whenever the selected file list changes.
onError
((error: FileUploadError) => void)
Fires when a file is rejected (type/size/count).
variant
"default"|"card"|"compact"|"minimal"
"default"
Visual treatment for the default (un-customized) trigger. - `default` — large dashed drop zone with primary Browse button. The main affordance. - `card` — solid bordered surface, horizontal layout. Fits in settings pages. - `compact` — single dashed row. For dense forms. - `minimal` — button-shaped trigger only. For toolbars and chat composers.
id
string
name
string
className
string
style
CSSProperties
children
ReactNode
Custom body content. When provided, replaces the default Trigger + List composition.
  • The hidden <input type="file"> carries the canonical accept / multiple / disabled flags so OS file dialogs behave correctly.
  • The drop zone exposes role="button" and tabIndex=0 so it’s reachable by keyboard; Enter and Space open the file dialog.
  • When accept is set, the trigger announces the accepted types via aria-describedby so screen-reader users know the constraint up front.
  • The file list is a <ul> with aria-live="polite" so additions and removals are announced without interrupting the user.
  • Each remove / preview action is an IconButton with a per-file aria-label (“Remove avatar.png”) to keep the action unambiguous.
  • Use variant="minimal" inside toolbars and chat composers where space is tight.
  • Pass onError to surface validation feedback in your own toast or ErrorText.
  • Provide renderItem on FileUploadList for fully-custom rows, or onPreview to add a preview button before the remove button.
  • Pair with name to participate in a native <form> — the underlying input is submitted as a real file field.