Dots

File Trigger

A button or a drop zone that adds files from the system picker, the camera, a drag and drop, or a paste.

Add a receiptPDF or image, up to 10 MB
"use client"

import * as React from "react"

FileTriggerButton is a kit Button that opens the system file picker, or the camera on phones. UploadDropZone is a one-row surface that also takes dropped and pasted files and has its own choose button. Use them to add receipts, statements and ID documents. Pick the button where space is tight or a single file is expected, and the drop zone on a page or sheet where people may drag files in. Both check every file against acceptedFileTypes and hand the wrong ones to onSelectInvalid. Show what was added with FileUploadItem.

Installation

pnpm dlx shadcn@latest add @dots/file-trigger

Usage

import { FileTriggerButton, UploadDropZone } from "@/components/ui/file-trigger"
<FileTriggerButton
  acceptedFileTypes={["application/pdf"]}
  onSelect={(files) => upload(files)}
>
  Attach statement
</FileTriggerButton>
<UploadDropZone
  label="Add a receipt"
  description="PDF or image, up to 10 MB"
  acceptedFileTypes={["image/jpeg", "image/png", "application/pdf"]}
  allowsMultiple
  onSelect={(files) => upload(files)}
  onSelectInvalid={(files) => showError(files)}
/>

Features

  • FileTriggerButton takes every Button prop: variants, sizes, isPending and a leading icon
  • UploadDropZone takes dropped files on desktop and pasted files anywhere, with a choose button on the same row
  • Accepted types as exact MIME types (application/pdf), wildcards (image/*) or extensions (.heic)
  • Chosen and dropped files are checked again after the picker, since pickers allow "All files"
  • Files of the wrong type go to onSelectInvalid instead of being dropped silently
  • Opens the front or back camera on phones with defaultCamera
  • isPending shows a spinner and blocks choosing, dropping and pasting
  • Exports isAccepted and getFileDropOperation for custom drop targets

Button

Use FileTriggerButton where a single button fits, such as an attach action in a form row. Put a leading icon in children with data-icon="inline-start".

No file chosen

"use client"

import * as React from "react"

Drop zone

UploadDropZone is the default way to add files on a page or in a sheet. Its label names the zone, description lists the accepted formats and size, and triggerLabel sets the button text. Pass icon to change the tile. The fill deepens while files are dragged over it.

Add your IDPhoto or scan, JPG or PNG

No file added

"use client"

import * as React from "react"

Camera

Set defaultCamera to "environment" for the back camera, for documents and receipts, or "user" for the front camera, for a selfie. Desktop browsers ignore it and open the file picker.

No photo yet

"use client"

import * as React from "react"

Wrong file types

Files that do not match acceptedFileTypes never reach onSelect. Pass onSelectInvalid to tell people what was skipped and what to send instead. Without it they are ignored.

Add proof of addressJPG, PNG or PDF

Nothing added yet

"use client"

import * as React from "react"

Some files, such as HEIC photos on some systems, report no MIME type. List their extension too:

<UploadDropZone acceptedFileTypes={["image/jpeg", "image/png", ".heic"]} />

Pending and disabled

Set isPending while a previous file is still being sent. It shows a spinner on the choose button and blocks choosing, dropping and pasting. Use isDisabled when adding files is not possible at all.

Add a receiptSending the previous file
Uploads are pausedTry again after the current check
import {
  FileTriggerButton,
  UploadDropZone,

Accessibility

FileTriggerButton is built on React Aria FileTrigger, which wraps a kit Button and a hidden file input. UploadDropZone is a React Aria DropZone: it is focusable, takes pasted files from the keyboard, and has a choose button for people who cannot drag.

  • Give an icon-only FileTriggerButton an aria-label.
  • A string label on UploadDropZone is also the accessible name of the drop and paste target. If you pass other content, set aria-label.
  • The description is linked to the choose button with aria-describedby, so the accepted formats are read with it.
  • Report skipped files in text near the zone, for example in a polite live region, so the result is not silent.

API Reference

FileTriggerButton

A kit Button that opens the file picker. Takes every Button prop.

PropTypeDefaultDescription
acceptedFileTypesReadonlyArray<string>-MIME types, wildcards or extensions. Filters the picker and checks the files.
allowsMultipleboolean-Lets people choose more than one file.
defaultCamera"user" | "environment"-Opens the camera instead of the file picker on phones.
onSelect(files: File[]) => void-Called with the chosen files that match acceptedFileTypes.
onSelectInvalid(files: File[]) => void-Called after onSelect with the files of the wrong type.
...propsReact.ComponentProps<typeof Button>-Props spread to the underlying Button.

UploadDropZone

A one-row surface that takes dropped, pasted or chosen files. Takes React Aria DropZone props except children, className, onDrop and getDropOperation.

PropTypeDefaultDescription
labelReact.ReactNode"Drop files or choose"Title of the zone. A string is also its accessible name.
descriptionReact.ReactNode-Accepted formats and size. Also describes the choose button.
triggerLabelReact.ReactNode"Choose"Text of the choose button.
iconReact.ReactNodeUpload iconIcon in the tile.
acceptedFileTypesReadonlyArray<string>-MIME types, wildcards or extensions.
allowsMultipleboolean-Takes more than one file. Otherwise only the first dropped file is used.
defaultCamera"user" | "environment"-Opens the camera from the choose button on phones.
onSelect(files: File[]) => void-Called with the chosen or dropped files that match acceptedFileTypes.
onSelectInvalid(files: File[]) => void-Called after onSelect with the files of the wrong type.
isPendingboolean-Shows a spinner on the button and blocks choosing, dropping and pasting.
isDisabledboolean-Disables the zone and the button.
classNamestring-Additional classes to apply to the zone.

isAccepted

isAccepted(file: File, types?: ReadonlyArray<string>): boolean returns whether a file matches the accepted types. No types accepts everything.

getFileDropOperation

getFileDropOperation(types, acceptedFileTypes?, allowedOperations?) returns "copy" or "cancel" for a React Aria drop target. Exact MIME types are checked while dragging; wildcards and extensions are checked on drop. Use it as getDropOperation on your own DropZone.