Skip to content

File Upload

A drop zone with a browse button and the list of chosen files, with size and type checks.

Preview

Receipts
Drag files here or PNG, JPG, PDF, up to 10 MB
Attach the receipts for this expense claim.

Installation

pnpm add @syntara/react @syntara/tokens
import { FileUpload } from '@syntara/react';

Usage

import { FileUpload } from '@syntara/react';

export function Receipts() {
  return (
    <FileUpload
      label="Receipts"
      acceptedFileTypes={['image/png', 'image/jpeg', 'application/pdf']}
      maxSize={10 * 1024 * 1024}
      allowsMultiple
      onChange={(files) => console.log(files)}
    />
  );
}

Examples

Progress and errors

Pass FileUploadEntry objects to show upload progress, a finished state, or a per-file error.

Supporting documents
Drag files here or Images, PDF, up to 20 MB
  • invoice-march.pdf842 kB
    Uploaded
  • site-photo-01.jpg2.3 MB
    64%
  • site-photo-02.jpg1.9 MB
    Upload failed. Check your connection and try again.

Single file

Without allowsMultiple a new file replaces the current one. Prompt copy is customisable.

Profile photo
Drop a photo here or PNG, JPG, up to 2 MB

Invalid and disabled

A field-level error, and a disabled upload.

Identity document
Drag files here or PDF
Upload a document to continue.
Contract
Drag files here or
Uploads are closed for this case.

Accessibility

KeysAction
TabFocuses the drop zone, then the browse button, then each file's remove button.
EnterorSpace (browse)Opens the system file dialog.
Ctrl/⌘V (drop zone)Pastes files from the clipboard.
Enter (drop zone)Drops the item being dragged with keyboard drag and drop.
EnterorSpace (remove)Removes that file (or dismisses a rejected one).
  • The drop zone is labelled by the field label; the browse button is described by the hint, description and error.
  • Rejected files are listed with their reason in a role="alert" so the refusal is announced.
  • Upload progress is a labelled progressbar ("Uploading invoice.pdf"); finished and failed files show an icon and text, not colour alone.
  • Remove buttons are named per file ("Remove invoice.pdf").
  • Drag-over is shown by a solid brand border, tint, halo and a filled upload badge, not motion alone. The swell, badge lift, staggered row rise and progress glide only run without prefers-reduced-motion; fades remain.

Guidelines

Do

  • State accepted types and the size limit up front (the default hint does this).
  • Show progress and a clear per-file error with a way to remove and retry.
  • Validate again on the server — the client checks are for fast feedback, not security.

Don’t

  • Don't make the drop zone the only way in — keep the browse button.
  • Don't silently drop files that fail a check — say why.

API reference

FileUpload

label
ReactNode

Visible label; also names the drop zone and the file list.

description
ReactNode

Help text under the drop zone, linked to the browse button.

errorMessage
ReactNode

Field-level error, shown under the drop zone when isInvalid is true.

acceptedFileTypes
string[]

MIME types or extensions, e.g. ['image/*', '.pdf']. Filters the file dialog; dropped files that don't match are rejected with a message.

allowsMultiple
boolean

Allow more than one file. Without it a new file replaces the current one.

Default false

maxSize
number

Maximum bytes per file. Larger files are rejected with a message. Sizes display 1024-based (1 MB = 1,048,576 bytes).

files
ReadonlyArray<File | FileUploadEntry>

Controlled list. FileUploadEntry = { file, progress?: 0–100, error?: string } adds a progress bar, an "Uploaded" state at 100, or an error.

defaultFiles
File[]

Initial files when uncontrolled.

onChange
(files: File[]) => void

Called with the full list of accepted files after every add or remove.

onReject
(rejections: FileRejection[]) => void

Called with files that were refused: { file, reason: 'type' | 'size' | 'count', message }.

hint
ReactNode

Text under the prompt. Defaults to the accepted types and max size, e.g. "PNG, PDF, up to 10 MB".

dropLabel
ReactNode

Prompt text before the browse link.

Default 'Drag files here or'

browseLabel
ReactNode

Text of the browse link that opens the file dialog.

Default 'browse'

isInvalid / isDisabled
boolean

Field states.

Default false

Tokens

The semantic tokens this component reads, grouped by what they control. Swatches show this site’s theme; change a tenant’s brand and the component follows with no code change.

Colour17
border.strongsurface.defaulttext.defaulttext.subtlefocus.ringaction.primary.bgsurface.selectedfeedback.danger.solidborder.defaultsurface.sunkentext.disabledsurface.raisedaction.primary.fgfeedback.danger.borderfeedback.danger.bgfeedback.danger.fgfeedback.success.fg
Type5
font.size.mdfont.weight.mediumline-height.snugfont.size.smfont.size.xs
Space and size6
space.2space.6space.4control-heightspace.1space.3
Shape3
radius.containerradius.pillradius.field
Depth3
shadow.raisedshadow.highlightshadow.overlay
Motion6
motion.duration.fastmotion.easingmotion.duration.slowmotion.easing-outmotion.duration.springmotion.spring