shadcn.io is an independent project built on shadcn/ui
An Upload Files label and a file field that accepts more than one file. After a pick, each row shows a file icon, the name, the size in KB, and a remove button.
Use it when people attach a few files before they submit. A dashed drop zone that keeps the list is File Upload. In React, Ark UI does not remove the input's file when the row goes away.
Installation
Anatomy

Usage
<Field className="w-full max-w-sm">
<FieldLabel>Upload Files</FieldLabel>
<Input
className="bg-background"
multiple
onChange={handleFileChange}
type="file"
/>
{files.length > 0 ? (
<ul className="flex flex-col gap-2">
{files.map((file, index) => (
<li
className="flex items-center justify-between gap-2 rounded-md border p-2"
key={`${file.name}-${file.lastModified}`}
>
<div className="flex min-w-0 items-center gap-2">
<FileTextIcon
aria-hidden="true"
className="size-4 shrink-0 text-muted-foreground"
/>
<span className="truncate text-sm">{file.name}</span>
<span className="shrink-0 text-muted-foreground text-xs">
({(file.size / 1024).toFixed(1)} KB)
</span>
</div>
<Button
aria-label={`Remove ${file.name}`}
data-index={index}
onClick={handleRemove}
size="icon-xs"
type="button"
{/* The rest of the file is under Installation. */}FAQ
How does the field accept several files?
multiple on the file input lets the picker select more than one. Each time the selection changes, the chosen files are copied from the input into a list kept in state, which renders the rows.
What does each row show?
A FileTextIcon, the file name, truncated when it's long, and the size in kilobytes with one decimal, from the file's size divided by 1024. The rows are a list with a border and rounded corners.
How does the remove button work?
It's a ghost Button at size icon-xs with an XIcon. It carries its row's index in data-index and removes that file from the list. Its aria-label names the file, such as Remove report.pdf.
Does removing a row change what the input holds?
No. The list is the example's own state; the browser's file input still holds the files from the last pick. Submit the list from state, for example with FormData, rather than the input itself.
API Reference
Field
No extra props in this demo. The rest is on the Field page.
Input
Sets multiple, onChange={handleFileChange} and type="file". The rest is on the Input page.
Button
Sets aria-label, Remove, file, name, data-index={index} and onClick={handleRemove}. The rest is on the Button page.





