Skip to content

File Upload

Upload files with clear rules, validation, progress and retry controls.

import { FileRejection, FileUpload, UploadContext, UploadFile, UseFileUploadOptions } from '@youj/react'

Installation & usage

npm i @youj/react

For an app already set up with npx @youj/cli init, which installs the peers (@youj/core, @youj/motion, motion and Base UI) and wires the provider. New app? Start at Installation.

Use it wherever people attach files: receipts on an expense, screenshots on a bug report, a spreadsheet to import, photos for a listing. The zone states the rules before anyone picks a file, a rejection says which file broke which rule, and each accepted file shows its own progress, a check when it's done, or the error with a Retry. For a single profile photo with cropping, use Avatar Upload.

import { FileUpload } from '@youj/react'

export function Attachments() {
  return (
    <FileUpload
      accept=".png,.jpg,.jpeg,.pdf"
      maxSize={5 * 1024 * 1024}
      maxFiles={6}
      onUpload={(file, { signal, onProgress }) => uploadToStorage(file, { signal, onProgress })}
    />
  )
}

The zone is a button and a drop target: click it, press Enter or Space, or drag files onto it. Its second line is generated from the rules, such as "PNG, JPG or PDF · up to 5 MB each", and hint replaces it. title sets the main line.

Reference

Propsvariant, multiple, title… (10)

Props

FileUpload props
PropType
variant'list' | 'gallery'

'list' (default): rows with progress. 'gallery': a grid of image tiles.

default 'list'

'list'
multipleboolean

Allow several files.

default true

true
disabledboolean

default false

false
titleReactNode

The zone's main line.

–
hintReactNode

Overrides the generated rules line ("PNG or JPG · up to 5 MB each").

–
acceptstring

As the input's accept: extensions (.pdf), MIME types (image/png) or wildcards (image/*).

–
maxSizenumber

In bytes.

–
maxFilesnumber

Total files allowed.

–
onUpload(file: File, context: UploadContext) => Promise<unknown>

Uploads one file. Resolve when done; reject (or throw) to show the error. Without it, files are only collected.

–
onFilesChange(files: UploadFile[]) => void–
Other props pass through: extends ComponentProps<'div'>.

The onUpload, onFilesChange, accept, maxSize and maxFiles options are the same for useFileUpload. Each file in onFilesChange is an UploadFile: the File, its status (uploading, done or error), progress, error and an image preview URL.

MotionDrag over, file joins, rows make room… · reduced motion

Motion

Motion tokens
MomentTokens
Drag overthe zone’s border turns accent and the upload icon lifts
duration.fastease.standard

140ms · cubic-bezier(0.2, 0, 0, 1)

140mscubic-bezier(0.2, 0, 0, 1)
File joinsthe row fades in with a 6px drop while the rows below glide (Presence List)
duration.baseease.enter

220ms · cubic-bezier(0.16, 1, 0.3, 1)

220mscubic-bezier(0.16, 1, 0.3, 1)
Rows make room
spring.smooth

0.36s, bounce 0

0.36s, bounce 0
Progressthe bar glides to each report, retargeting mid-glide
spring.smooth

0.36s, bounce 0

0.36s, bounce 0
Donethe row settles into a drawn check
duration.slowease.enter

340ms · cubic-bezier(0.16, 1, 0.3, 1)

340mscubic-bezier(0.16, 1, 0.3, 1)
Rejectionsthe alert grows in under the zone, as a Field error does
duration.baseease.standard

220ms · cubic-bezier(0.2, 0, 0, 1)

220mscubic-bezier(0.2, 0, 0, 1)
File removedfades in place, then the rest close up
duration.fastease.exit

140ms · cubic-bezier(0.4, 0, 1, 1)

140mscubic-bezier(0.4, 0, 1, 1)

Reduced motion

Rows and tiles appear and go at once, progress jumps to each value and the check is already drawn.

Reduced motion

Rows and tiles appear and disappear without fading or gliding, progress bars jump to each value and the done check shows already drawn. Drag-over still changes the border.

AccessibilityKeyboard, focus, announcements

Accessibility

  • The drop zone is a real button, so Enter and Space open the file picker, and its description reads the rules.
  • Every change is announced politely: files added, rejections, each upload finishing or failing, and removals.
  • Rejections are listed through a Field error under the zone, one line per file with its reason.
  • Each row's buttons are named with the file name: "Cancel upload of receipt.pdf" while it uploads, then "Remove receipt.pdf", and "Retry receipt.pdf" after a failure.
  • Focus never drops to the page: removing a row moves focus to the next row's button, or back to the zone when the list empties.