Built on Vercel’s UI SDK 0.1.0-beta.2. Its API may change.
File input
A file input on Vercel's UI SDK with a drop zone, paste, type and size checks, rejected files with reasons, image previews and form support.
- Form components
- 14 parts
- 12 examples
@tabler/icons-react@ui-sdk/core@ui-sdk/reactclass-variance-authority
Drop files here
Images or PDFs, up to 5 files of 5 MB each. You can paste them too.
pnpm dlx shadcn@latest add https://hextaui.com/r/ui-sdk-file-input.jsonAdds the component, the HextaUI theme tokens and any HextaUI components it depends on.
Add the theme tokens to your global CSS, if you haven’t yet.
Install the dependencies.
pnpm add @ui-sdk/react@0.1.0-beta.2 @ui-sdk/core@0.1.0-beta.2 @tabler/icons-react class-variance-authority cnCopy and paste the following code into your project.
components/ui/ui-sdk-file-input.tsx components/ui/button.tsx Update the import paths to match your project setup.
UI SDK adds a little CSS of its own in a cascade layer named ui-sdk. Declare that layer at the top of your global stylesheet, above the Tailwind import, so your classes win over it.
The file input holds the files people choose, drop or paste, and submits them with a form. Uploading them is up to your app. FileInputItem with no children renders a finished row with a preview, the name, the size and a remove button, once for each file in the list.
The checks for accept, sizes, counts and validate run in the browser only, to tell people early why a file was left out. Anyone can skip them, so check every upload again on your server.
accept takes MIME types, wildcards and extensions. maxFiles, minSize and maxSize set the limits, and validate adds checks of your own. FileInputRejections lists each file that was left out with every reason, until the next accepted file.
FileInputItemPreview shows an image from an object URL it revokes when the file goes away, and an icon for other types. With layout="grid", each file is a square tile.
A trigger alone opens the system file picker. Without multiple, a new file replaces the current one, and the root’s children can be a function of its state.
With variant="composer", the drop zone is the card around a message box, and layout="chips" lists files as compact chips. pasteTarget takes pasted screenshots from the text area, while pasted text stays text.
With target="document" and variant="overlay", the zone covers the page while files are dragged over the window, and takes files dropped anywhere. A drop on another zone stays with that zone.
directory picks a whole folder and turns on multiple. Item names show the path inside the folder.
Pass value and onValueChange. A controller from createFileInputController() opens the picker from any button, and clears or adds files.
With a name, the files submit through a native file input, even before the page hydrates. Rejected files never submit, and a form reset empties the list.
invalid marks the drop zone and the trigger, and the trigger tells screen readers. Point the trigger’s aria-describedby at your message.
A disabled file input ignores picks, drops and pastes, and submits nothing.
Rows and the drop zone mirror, file names keep their own direction, and sizes format in the locale you pass.
| Key | Action |
|---|---|
| Tab | Moves to the trigger, then into the list, onto the last focused file or the first one. |
| EnterSpace | Opens the file picker from the trigger. |
| ↑↓←→ | Moves between files in the list. |
| HomeEnd | Moves to the first or last file. |
| DeleteBackspace | Removes the focused file and focuses the next one. |
| ⌘V | Pastes files while focus is inside the drop zone or its paste target. |
The drop zone isn’t a control, so always keep a trigger beside it for people who use a keyboard or a screen reader. The list is one tab stop, and each remove button is named for its file, like “Remove photo.png”, and stays out of the tab order because Delete on the item does the same.
Accepted, removed and rejected files are announced through a polite live region in English. Pass announcement to say it in your language, and getReasonLabel to FileInputRejections for its reasons. The drop zone icon only lifts when motion is allowed, and new rows fade in only then too.
Each part wraps the UI SDK part of the same name and accepts all of its props, including render and a className that can be a function of the part’s state.
| Prop | Type | Default |
|---|---|---|
value | File[] | – |
defaultValue | File[] | [] |
onValueChangedetails has the reason and the added, removed and rejected files. | (value: File[], details) => void | – |
onRejectRuns when files fail the checks, even when none were accepted. | (rejected: FileRejection[], details) => void | – |
accept | string | string[] | – |
multiple | boolean | false |
maxFiles | number | – |
maxSizeIn bytes. | number | – |
minSizeIn bytes. | number | – |
validateReturn a reason to reject the file, after the built-in checks. | (file: File) => string | null | – |
directory | boolean | false |
captureOpens the camera on phones. | "user" | "environment" | boolean | – |
invalid | boolean | false |
disabled | boolean | false |
required | boolean | false |
name | string | – |
form | string | – |
announcementWhat screen readers hear after a change. | (change) => string | null | – |
controllerFrom createFileInputController(). | FileInputController | – |
children | ReactNode | (state) => ReactNode | – |
| Attribute | Description |
|---|---|
data-slot="file-input" | Target the file input in CSS. |
data-dragging | Present while files are dragged over one of its drop zones. |
data-invalid | Present when invalid. |
data-required | Present when a file is required. |
data-disabled | Present when disabled. |
| Prop | Type | Default |
|---|---|---|
variantA dashed zone, a card that wraps a message composer, or a full-page overlay shown while dragging. | "default" | "composer" | "overlay" | "default" |
target | "element" | "document" | "element" |
pasteTargetSomewhere else to take pasted files from. | "document" | string | Element | () => Element | – |
render | ReactElement | (props, state) => ReactElement | – |
| Attribute | Description |
|---|---|
data-slot="file-input-dropzone" | Target the drop zone in CSS. |
data-variant | The variant: default, composer or overlay. |
data-dragging | Present while files are dragged over it. |
data-drag-accept | Present while the dragged files look acceptable, from their types and count. |
data-drag-reject | Present while a dragged file has a type that isn’t accepted, or there are too many. |
data-disabled | Present when disabled. |
| Prop | Type | Default |
|---|---|---|
variant | Button variant | "outline" |
size | Button size | "sm" |
controllerConnects a trigger outside the file input, like one in a toolbar. | FileInputController | – |
render | ReactElement | (props, state) => ReactElement | – |
| Prop | Type | Default |
|---|---|---|
layoutRows, square image tiles, or compact chips. Items take their look from it. | "list" | "grid" | "chips" | "list" |
render | ReactElement | (props, state) => ReactElement | – |
| Attribute | Description |
|---|---|
data-slot="file-input-list" | Target the list in CSS. |
data-layout | The layout: list, grid or chips. Items carry it too. |
| Prop | Type | Default |
|---|---|---|
childrenDefaults to a preview, the name, the size and a remove button. The state has the file and its index. | ReactNode | (state) => ReactNode | – |
render | ReactElement | (props, state) => ReactElement | – |
| Attribute | Description |
|---|---|
data-slot="file-input-item" | Target a file’s row in CSS. |
data-disabled | Present when disabled. |
| Attribute | Description |
|---|---|
data-slot="file-input-item-preview" | Target the preview in CSS. |
data-kind | image for an image preview, icon for an icon by file type. |
| Prop | Type | Default |
|---|---|---|
localeFormats the size, like 2.4 kB, in this locale. Defaults to the browser’s. | string | – |
| Prop | Type | Default |
|---|---|---|
childrenDefaults to an X icon. | ReactNode | – |
render | ReactElement | (props, state) => ReactElement | – |
| Prop | Type | Default |
|---|---|---|
getReasonLabelNames each reason. Defaults to English, like “Larger than 5 MB”. | (reason, rules, rejection) => string | – |
| Attribute | Description |
|---|---|
data-slot="file-input-rejections" | Target the list of rejected files in CSS. |
- ButtonButtons in every variant and size, with a built-in loading, success and error flow that skips the spinner for fast requests.
- CalendarA date grid for single, range and multiple selection, with sliding months, range previews and touch-sized days.
- CheckboxA checkbox whose check draws in, with indeterminate parents, groups and labels that share its hover.
- ComboboxA filterable select with chips, groups and async results, in a popup that resizes as you type.
- Date pickerA button that opens a calendar in a popover, or a bottom sheet on phones, for single dates and ranges.
- FieldLabels, descriptions and errors wired to their control, with validation states and layouts for forms.