Skip to content

FilePicker

A dropzone you drag files onto, or press to open the file dialog. It checks size, type and count, and reports back what it turned away.

tsx
import { FilePicker } from 'neba';

<FilePicker
  multiple
  label="Attachments"
  accept="image/*,.pdf"
  maxSize={5_000_000}
  maxFiles={4}
  onFilesChange={setFiles}
  onReject={setRejected}
/>;

Props

PropTypeDefaultDescription
variantshared'solid' | 'outline' | 'text''outline'Weight of the surface. All three share the dashed edge: the one place the library draws a line that is not solid, because a dashed rectangle is the established sign for a drop target
sizeshared'xs' | 'sm' | 'md' | 'lg' | 'xl''md'The box's padding and type scale. A dropzone is sized by the gesture it has to catch, not by what is written in it
colorshared'primary' | 'secondary' | 'success' | 'warning' | 'danger' | 'info''primary'Semantic colour role. Arbitrary colour values are not accepted
densityshared'default' | 'compact''default'Padding only: never the height, never the type scale
elevationshared0 | 1 | 2 | 30Drop shadow depth. 0 means no shadow at all
acceptstringWhich files the browser's own picker offers: 'image/*,.pdf'. Dropped files are checked against it too, which the attribute alone does not do
multiplebooleanfalseWhether more than one file may be chosen
maxSizenumberThe largest a single file may be, in bytes
maxFilesnumberHow many files may be held at once: counted against what is already chosen, not against one drop
valuereadonly File[]The chosen files, for a controlled picker
defaultValuereadonly File[]The initially chosen files
onFilesChange(files: File[]) => voidCalled when the list of files changes
onReject(rejections: FileRejection[]) => voidCalled with everything turned away, and why. Without it a rejected file disappears silently, which is the worst thing a dropzone does
titleReactNodeThe line inside the box
hintReactNodeThe line under it: what is accepted, how big, how many
iconReactNodeThe glyph above the title. Pass null for a box with no picture in it
showListbooleantrueLists the chosen files under the box, each with a way to remove it
removeLabel(name: string) => stringAccessible name of a file's remove button
labelReactNodeThe label, wired to the control by Base UI's Field
descriptionReactNodeHelper text
errorReactNodeError message. Its presence turns the control invalid and re-points the colour family at danger
invalidboolean!!errorForces the invalid state without a message, for when a form library owns validity
readOnlybooleanfalseShown but not changeable. Keeps its colour and edge, drains the saturation
disabledbooleanfalseUnavailable. Drops the colour family for neutral grey
fullWidthbooleantrueStretches to the width of the container
requiredbooleanfalseWhether a file must be chosen before the form is submitted
namestringIdentifies the field when a form is submitted

Native <div> attributes pass through to the root. Only color, defaultValue and title are excluded, since the table above spells them differently.

Examples

variant

All three weights share the dashed edge, which is the established sign that an area accepts a drop.

accept · maxSize · maxFiles

The browser's accept attribute only governs its own file dialog and never a file that arrived by drag, so this component runs the same string itself. All three forms are supported: .ext, type/subtype and type/*.

maxFiles is not how many may be dropped at once but how many may be held in total. Dropping three files on a maxFiles={3} picker that already holds two accepts exactly one.

onReject

Reports the files that were turned away and why. Without this handler a rejected file disappears with no feedback, so always pass it.

disabled · readOnly · error

title · hint · icon · showList

title and hint are the copy inside the dropzone and icon the glyph above it. showList renders the chosen files under the zone, and removeLabel names each remove button.

Accessibility

  • The shell is a <div> and the pressable area inside it is a real <button>. The file list sits outside that button, so the remove buttons are never nested inside the browse button.
  • The <input type="file"> is moved off-screen rather than set to display: none, which makes an input unfocusable in some browsers and would block required validation messages.
  • The drag state is tracked by counting events, so it does not flicker as the pointer crosses children of the dropzone.

Released under the MIT License