Skip to content

Components · Files

FileUpload

A drop zone and browse button that checks type, size and count, explains refusals and hands files to the product.

@rookcom/monarch-react/collaborationComponent

With an upload queue

Drag and drop files here

Images or PDF, up to 10 MB each, 5 files max

Usage examples

In a field, single file

Compact, and limit reached

The same checks without the zone

Purpose

Lets people choose files by dropping them, browsing or pasting, checks them against the limits the product sets, and gives the accepted files to the product, which uploads them.

Use when

  • A form or screen accepts attachments, images or documents: profile photos, résumés, banners, ticket attachments.
  • Limits on type, size or count must be explained before upload instead of failing on the server.
  • Pair with UploadQueue to show the progress the product reports.

Avoid when

  • Files are attached inline in a composer with its own attach button: use the composer’s button and call validateFiles yourself.
  • The user picks from files already stored in the product: use a picker of existing items.

Design principles

  • The component never uploads. It emits onFiles(accepted, rejected); the product transfers, and reports progress through UploadQueue.
  • The browse button is always the keyboard and screen reader path; the drop zone is an extra for pointer users and is not focusable.
  • Refusals are specific and name the file: "scan.png is larger than 10 MB.", not "Invalid file".
  • Limits are stated up front in the hint (generated from maxSize and maxFiles unless hint is given).
  • The same checks are available on their own as validateFiles, so composers and other entry points refuse files the same way.

API reference

import { FileUpload, fileMatchesAccept, formatBytes, validateFiles } from '@rookcom/monarch-react/collaboration';
ExportKindRenderingRoot class
FileUploadcomponentclient.mn-file-upload
fileMatchesAcceptutilityclient–
formatBytesutilityclient–
validateFilesutilityclient–

onFiles(accepted, rejected) runs on every pick, drop and paste. rejected items are { file, code, message }, with code one of file-invalid-type, file-too-large, too-many-files, custom.

accept uses the input syntax (image/*,.pdf,application/zip) and is checked again after the pick, because drops and pastes skip the browser’s filter.

maxFiles counts currentCount (files already attached); when it is reached the zone is disabled and says so.

validateFiles(files, options), fileMatchesAccept(file, accept) and formatBytes(bytes) are exported for use elsewhere.

Inside a Field, the browse button takes the Field label and description and reflects invalid.

The ref is the root <div>; other div attributes and drag/paste handlers pass through.

Generated from the TypeScript declarations of packages/react/src/components/files/file-upload.

Properties

FileUpload

A drop zone with a browse button for choosing files. It checks type, size and count, shows clear messages for refused files, and hands the accepted ones to the product, which does the upload.

PropTypeDescription
acceptstringSame syntax as the input `accept` attribute: `image/*,.pdf,application/zip`.
capture"user" | "environment"The input `capture` attribute, for camera capture on phones.
classNamestring
currentCountnumberFiles already attached, which count towards `maxFiles`. Default 0.
disabledboolean
hintReactNodeText under the label. Default: generated from `maxSize` and `maxFiles`.
iconReactNodeIcon at the start of the zone. Decorative.
idstring
invalidbooleanMark the zone invalid, for example when a required file is missing.
maxFilesnumberMost files allowed in total, counting `currentCount`.
maxSizenumberLargest allowed size per file, in bytes.
messagesPartial<FileUploadMessages>Override labels and messages.
multiplebooleanAllow choosing several files. Default: true unless `maxFiles` is 1.
namestringName of the hidden file input.
onFiles((accepted: File[], rejected: FileRejection[]) => void)Called with every pick, drop or paste: the files that passed the checks and the ones that did not. The product uploads `accepted` and reports progress (for example through UploadQueue).
pastebooleanAlso accept files pasted (Ctrl/Cmd+V) while focus is inside the component.
showErrorsbooleanShow refusal messages under the zone (default true). Set false to show them yourself from `onFiles`.
validate((file: File) => string | null | undefined)Extra check. Return a message to refuse the file, or null to allow it.
variant"compact" | "zone"`zone` (default): a bordered drop area. `compact`: one line, for tight spaces.

Variants

PropValuesDescription
variant'zone''compact'zone (default): a centred, dashed drop area. compact: one row with the icon, text and button, for side panels and dense forms.
multiple'true''false'Default true unless maxFiles is 1. A single-file upload refuses extra dropped files.
paste'true''false'Also take files pasted while focus is inside the component.
showErrors'true''false'Show refusal messages under the zone (default), or leave them to the product.

States

StateExposed asBehaviour
Drag overdata-drag="accept"Files are over the zone: accent border and background, "Drop to upload".
Drag rejecteddata-drag="reject"The dragged files’ types do not match accept (when the browser exposes them): danger border, "This file type is not allowed".
Refused filesdata-invalidThe last pick had refused files: their messages are listed under the zone.
Disabled or fulldata-disableddisabled, or currentCount has reached maxFiles ("Limit reached"). The button is disabled and drops are ignored.
Focus–The browse button shows the Monarch focus ring on :focus-visible.

Interaction behaviour

  • Browse opens the system file picker. The hidden input is cleared after each pick, so choosing the same file again works.
  • Dropping files anywhere on the component takes them; dragging text or links over it does nothing.
  • Each pick replaces the previous refusal messages.
  • Files over the count limit are refused in order, after the type and size checks, so the first valid files are kept.
  • validate adds a synchronous check of your own (return a message to refuse). Checks that need the network, such as virus scanning, belong in the upload flow and show in UploadQueue.
  • On phones, capture="environment" or "user" opens the camera.

Keyboard support

KeysAction
TabMoves to the browse button.
Enter / SpaceOpens the system file picker.
Ctrl+V / Cmd+VWith paste: adds files from the clipboard while focus is inside.

Accessibility

Role
button (browse); the zone has no role
Accessible name
"Browse files" by default. Inside a Field it is "{Field label} Browse files".
  • The button is described by the hint, the Field description and any refusal messages, so they are read with it.
  • Refusal messages sit in a polite live region and are announced when they appear; accepted files are announced as "2 files added."
  • The file input is visually hidden, out of the tab order and hidden from assistive technology; the button is the single control.
  • The drop zone is never the only way in (people with keyboards, switches or voice use the button).
  • Verified with axe in jsdom and in Chromium, Firefox and WebKit on /components/file-upload.

Responsive behaviour

  • The zone fills its container and wraps its text; it works at 320px.
  • compact wraps the button under the text when the row is too narrow.
  • Long file names in messages break anywhere rather than overflow.

Theme support

  • Colours are Monarch semantic tokens only, so light, dark and more-contrast themes apply automatically.
  • The zone uses --mn-color-bg-subtle with a dashed --mn-color-border-strong border; drag states use the accent and danger subtle/strong pairs.
  • Reduced motion removes the border and background transition. Forced colours draw the drag state with Highlight.

Do and don't

Do

  • Set accept, maxSize and maxFiles to the limits the server enforces, so people learn them before uploading.
  • Keep checking on the server too: client checks are for clarity, not security.
  • Show the upload itself with UploadQueue, including failures and retry.

Don't

  • Do not rely on drag and drop alone: keep the browse button visible.
  • Do not show a generic "Upload failed" for a refusal the component already explained.
  • Do not put business rules about storage (buckets, keys, quotas) in the component; pass the resulting limits.
  • UploadQueue: Shows the progress, status and retry of the files this component hands over.
  • Field: Gives the browse button a label, description and invalid state.
  • Progress: A single progress bar, when a list is not needed.

Dependencies

KindDepends on
npm packagesreact
Monarch componentsButton, Field

Testing coverage

  • Accept rules (MIME, wildcard, extension), size, custom and count checks, with messages that name the file; byte formatting.
  • Browse opens the picker from the keyboard; picked files are split into accepted and refused; messages describe the button and are cleared on the next pick; the input is reset.
  • Single-file mode refuses extra files; drag-over accept and reject states; drops; paste only when enabled.
  • Disabled and limit-reached states ignore input; errors can be left to the product.
  • Field label, description and invalid state reach the button; compact variant, custom icon and translated messages; axe; server rendering.
CheckCovered
Axe in jsdomYes
Server renderingYes
Browser suitesVisual baselines: /__gallery/file-upload; Cross-browser axe: /components/file-upload

Tests: packages/react/src/components/files/file-upload