- Accordion
- Action Bar
- Action Toast
- Alert Banner
- Alert Dialog
- Alert
- Alphabet Index
- Amount Field
- Aspect Ratio
- Attachment
- Availability List
- Avatar
- Badge
- Bank Card
- Breadcrumb
- Bubble
- Button Group
- Button
- Calendar
- Camera Frame
- Card Design Picker
- Card Details
- Card Peek
- Card Stack
- Card
- Carousel
- Chart
- Chat Thread
- Checkbox
- Chip Group
- Choice Tile
- Code Block
- Code Dots
- Collapsible
- Combobox
- Command
- Context Menu
- Copy Field
- Countdown
- Country Select
- Data Table
- Date Field
- Date Picker
- Date Range Picker
- Dialog
- Direction
- Document Paper
- Drawer
- Dropdown Menu
- Empty
- Entity Avatar
- Field
- File Trigger
- File Upload Item
- Hover Card
- Input Group
- Input OTP
- Input
- Item
- Kbd
- Key Value
- Keypad
- Label
- Live Region
- Marker
- Message Scroller
- Message
- Money Amount
- Native Select
- Number Field
- Pagination
- Percent Split Editor
- Phone Field
- Popover
- Progress
- QR Code
- Questionnaire
- Radio Group
- Range Histogram
- Rating
- Resizable
- Scroll Area
- Search Field
- Select
- Separator
- Share Bar
- Sheet
- Sidebar
- Skeleton
- Slider
- Sonner
- Sortable List
- Spinner
- Status Timeline
- Step Header
- Steps List
- Swipe Row
- Switch
- Table
- Tabs
- Text Field
- Textarea
- Tier Ladder
- Toggle Group
- Toggle
- Token Search Field
- Tooltip
- Trace
- Transaction Row
- Usage Meter
"use client"
import * as React from "react"FileUploadItem shows one file in an upload form: waiting, uploading with progress, being checked, accepted, or rejected with a reason and a way to replace it. Use it for documents people send for review, such as statements, payslips and ID photos. Pick it over Attachment when each file goes through a check and may come back rejected. Add files with File Trigger.
Installation#
pnpm dlx shadcn@latest add @dots/file-upload-item
Usage#
import { FileUploadItem } from "@/components/ui/file-upload-item"<FileUploadItem
name="bank-statement-october.pdf"
state="uploading"
progress={44}
description="2.1 of 4.8 MB"
onCancel={() => cancel()}
/>Features#
- Five states:
queued,uploading,checking,acceptedandrejected - Progress bar and percentage while uploading
- Cancel while queued or uploading, remove once settled
- A rejection reason under the row and a replace button that opens the file picker
- A rejected row also takes a dropped or pasted replacement
- The file name truncates before the extension, so
.pdfstays visible - State changes are announced politely; progress is not
isDisabledlocks every action while the parent form submits- No surface of its own: stack rows in a card, sheet or form
States#
Set state to follow the upload. queued is a file waiting its turn, with no progress yet. uploading shows a bar when you pass progress from 0 to 100. checking shows a spinner. accepted shows a check mark before the status.
"use client"
import { FileUploadItem } from "@/components/ui/file-upload-item"The buttons appear only when you pass their handlers: onCancel for queued and uploading, onRemove for accepted and rejected.
Rejected#
Pass a reason that says why the file was rejected and what to send instead. With onReplace, a rejected row shows a replace button and also takes a dropped or pasted file. Replacements are checked against acceptedFileTypes; the wrong type goes to onReplaceInvalid. Check the size and content yourself in onReplace.
"use client"
import * as React from "react"Icon#
The tile shows a document icon by default. Pass icon to show the file type. While checking the tile shows a spinner, and once rejected a warning icon.
"use client"
import { FileUploadItem } from "@/components/ui/file-upload-item"Disabled#
Set isDisabled while the parent form submits. Cancel, remove, replace and dropping a replacement are disabled, and the row stays as it is.
"use client"
import * as React from "react"Labels#
Every state and button text can be changed through labels, for example to translate them. Button names get the file name appended, such as "Remove passport-front.jpg".
<FileUploadItem
name="passport-front.jpg"
state="checking"
labels={{ checking: "In review" }}
/>Accessibility#
The action buttons are kit Buttons, replace is a FileTriggerButton, and with onReplace the rejected row is a React Aria DropZone that takes a pasted file from the keyboard. Without onReplace the row is a plain div.
- Each button's accessible name includes the file name, so several rows stay distinct.
- State changes and a new rejection reason are announced through a polite live region. Progress is not announced; the
Progressbar carries its own value. - The
reasondescribes the replace button. A string reason is also announced with the rejected state. - When the replace button disappears because the new file starts uploading, focus moves to the row's next button instead of being lost.
- Keep the reason in words. The warning icon is decorative.
API Reference#
FileUploadItem#
Takes div props except children and onDrop.
| Prop | Type | Default | Description |
|---|---|---|---|
name | string | - | File name. Required. The extension never truncates. |
state | "queued" | "uploading" | "checking" | "accepted" | "rejected" | - | The upload state. Required. |
progress | number | - | 0 to 100 while uploading. Without it no bar is shown. |
description | React.ReactNode | - | Meta after the state, such as "2.1 of 4.8 MB" or "1.2 MB". |
reason | React.ReactNode | - | Why the file was rejected and what to send instead. |
icon | React.ReactNode | Document icon | Icon of the file type in the tile. |
onCancel | () => void | - | Shows the cancel button while queued or uploading. |
onRemove | () => void | - | Shows the remove button once accepted or rejected. |
onReplace | (file: File) => void | - | Shows replace on a rejected file and lets the row take a dropped file. |
onReplaceInvalid | (file: File) => void | - | Called instead of onReplace with a replacement of the wrong type. |
acceptedFileTypes | ReadonlyArray<string> | - | Accepted replacements: MIME types, wildcards or extensions. |
defaultCamera | "user" | "environment" | - | Opens the camera for the replacement on phones. |
isDisabled | boolean | false | Disables cancel, remove, replace and dropping a replacement. |
labels | Partial<FileUploadItemLabels> | - | Copy for states and buttons. |
className | string | - | Additional classes to apply to the row. |
FileUploadState#
type FileUploadState = "queued" | "uploading" | "checking" | "accepted" | "rejected"isAccepted#
isAccepted(file, acceptedFileTypes) is re-exported from File Trigger, so you can run the same type check on files you add yourself.
FileUploadItemLabels#
| Key | Default |
|---|---|
queued | "Waiting" |
uploading | "Uploading" |
checking | "Checking" |
accepted | "Accepted" |
rejected | "Rejected" |
cancel | "Cancel upload" |
remove | "Remove" |
replace | "Replace" |
dropzone | "Drop or paste a file to replace" |