File Upload
A button-triggered file picker paired with a list of selected files, each showing its name, size, upload progress, and a remove control.
Install
Pull File Upload from the barrel for everyday use, or the granular path when you want a tighter bundle.
import { FileUpload } from "@brika/clay";import { FileUpload } from "@brika/clay/components/file-upload";A minimal example
Drop this into a page. Native HTML attributes pass through to the underlying primitive.
'use client';
import { Button } from '@brika/clay/components/button';
import {
FileUpload,
FileUploadItem,
FileUploadItemContent,
FileUploadItemName,
FileUploadItemPreview,
FileUploadItemRemove,
FileUploadItemSize,
FileUploadList,
FileUploadTrigger,
} from '@brika/clay/components/file-upload';
import { FileText, ImageIcon, Upload } from 'lucide-react';
import { useState } from 'react';
interface PickedFile {
readonly id: string;
readonly name: string;
readonly size: number;
}
function toPicked(file: File): PickedFile {
return { id: `${file.name}-${file.size}`, name: file.name, size: file.size };
}
/** Pick files, then list each with its name, size, and a remove button. */
export default function FileUploadDefaultDemo() {
const [files, setFiles] = useState<PickedFile[]>([
{ id: 'a', name: 'quarterly-report.pdf', size: 2_411_724 },
{ id: 'b', name: 'cover-photo.jpg', size: 845_120 },
]);
const addFiles = (selected: File[]) => setFiles((prev) => [...prev, ...selected.map(toPicked)]);
const removeFile = (id: string) => setFiles((prev) => prev.filter((f) => f.id !== id));
return (
<FileUpload className="w-full max-w-md" multiple onFilesSelected={addFiles}>
<FileUploadTrigger asChild>
<Button variant="outline">
<Upload aria-hidden />
Choose files
</Button>
</FileUploadTrigger>
{files.length > 0 && (
<FileUploadList>
{files.map((file) => (
<FileUploadItem key={file.id}>
<FileUploadItemPreview>
{file.name.endsWith('.jpg') ? <ImageIcon aria-hidden /> : <FileText aria-hidden />}
</FileUploadItemPreview>
<FileUploadItemContent>
<FileUploadItemName>{file.name}</FileUploadItemName>
<FileUploadItemSize bytes={file.size} />
</FileUploadItemContent>
<FileUploadItemRemove onClick={() => removeFile(file.id)} />
</FileUploadItem>
))}
</FileUploadList>
)}
</FileUpload>
);
}File Upload, every way
With Progress
Show per-file upload progress with an inline progress bar.
Accessibility
- The native file
<input>is visually hidden but stays in the DOM, so the trigger and assistive tech reach the OS file picker. -
FileUploadTriggerrenders a real<button>(or your own element viaasChild) and forwards focus and keyboard activation. -
FileUploadItemRemovecarries anaria-label(defaults to "Remove file") so its purpose is announced. - Progress bars expose
role="progressbar"with the current value through the underlying Progress primitive.
Theme tokens
Every CSS variable File Upload reads, with its default and the
dotted path you'd write in a ThemeConfig JSON
to override it. Set any of these in your theme to retune File Upload
without touching component code.
File Upload tokens
14 tokens--file-upload-item-containercolorvar(--card)components.fileUpload.itemContainerBackground of a file-list row.
--file-upload-item-bordercolorvar(--border)components.fileUpload.itemBorderBorder color of a file-list row.
--file-upload-preview-bgcolorvar(--muted)components.fileUpload.previewBgBackground of the leading preview/icon tile on each row.
--file-upload-iconcolorvar(--muted-foreground)components.fileUpload.iconColor of the file-type glyph inside the preview tile.
--file-upload-namecolorvar(--foreground)components.fileUpload.nameFile name text color.
--file-upload-metacolorvar(--muted-foreground)components.fileUpload.metaSecondary text color for file size and status.
--file-upload-radiusradiusvar(--radius-control)components.fileUpload.radiusCorner radius of a file-list row and its preview tile.
--file-upload-padding-xsizecalc(var(--spacing) * 3)components.fileUpload.paddingXInline padding inside the file-upload.
--file-upload-padding-ysizecalc(var(--spacing) * 2)components.fileUpload.paddingYBlock padding inside the file-upload.
--file-upload-gapsizecalc(var(--spacing) * 3)components.fileUpload.gapGap between adjacent children inside the file-upload.
--file-upload-border-widthborder-width1pxcomponents.fileUpload.borderWidthBorder width on the file-upload. Set non-zero for outline-style variants.
--file-upload-border-styleborder-stylesolidcomponents.fileUpload.borderStyleBorder style on the file-upload (`solid`, `dashed`, `double`, `none`).
--file-upload-durationdurationvar(--motion-standard-duration)components.fileUpload.durationTransition duration for file-upload state changes.
--file-upload-easingeasingvar(--motion-standard-easing)components.fileUpload.easingTransition easing for file-upload state changes.
Override in a theme
Authoring a theme is plain JSON. Drop overrides under
components.fileUpload, the names
are camelCase versions of the variable suffix.
{
"id": "my-theme",
"name": "My Theme",
"description": "...",
"accentSwatches": ["#000"],
"components": {
"fileUpload": {
"paddingX": "calc(var(--spacing) * 3)"
}
}
}
Then apply with applyTheme(myTheme)
(see the theming guide),
scope it to a subtree with themeToCssVars(myTheme, mode),
or consume any token directly in your own JSX with the matching Tailwind utility
(e.g. bg-file-upload-…) — see the
chips on each token row.
API reference
Props specific to File Upload. Native HTML attributes pass through to the underlying primitive, see the component source on GitHub for the full type signature.
<FileUpload />
4 props- stringComma-separated list of accepted types, forwarded to the native input.
- disableddefault
falsebooleanDisable the trigger and the underlying input. - multipledefault
falsebooleanAllow selecting more than one file at a time. - (files: File[]) => voidFires with the chosen files whenever the picker resolves.
<FileUploadContext />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.
<FileUploadItem />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.
<FileUploadItemContent />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.
<FileUploadItemName />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.
<FileUploadItemPreview />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.
<FileUploadItemProgress />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.
<FileUploadItemRemove />
1 prop- aria-labeldefault
'Remove file'stringNo description.
<FileUploadItemSize />
1 prop- numberByte count, formatted via
formatFileSizewhen no children are given.
<FileUploadList />
passthroughNo wrapper-specific props, all attributes pass through to the underlying primitive.
<FileUploadTrigger />
1 prop- asChilddefault
falsebooleanRender the trigger behavior onto your own child element (e.g. a Button).