File List React component for Tailwind CSS v4
v1.0.0Added in 1.0.0
A React file list for attachments and documents: rows, preview tiles or chips with type icons, thumbnails, upload progress and file actions.
Click or tap a file to preview it (photos open in place in the demo, documents open their url in a new tab by default); the row tints on hover and press. Photos fade in as they load, and a picture that fails falls back to its type tile. On desktop the download button sits at the end of each row and the ⋯ menu (shown on hover or focus, always on touch) holds the extra actions and Delete, which asks first in an alert dialog and fades the row while it runs; right click opens the same menu. When the list is narrower than 448px (a phone, a sidebar card) download moves into the ⋯ menu and uploaders show as a face only; on a phone the menu opens as a bottom sheet, and a long press opens it too. Uploading files show a thin progress bar with "64% · 2,4 of 3,8 MB" and a cancel ×; processing shows a moving bar; a failed upload shows its reason in red with Retry and remove; when an upload finishes its progress crossfades into the size and time with a quick check mark. With selectionMode="multiple" each file gets a checkbox: click it, Shift + click for a range, or long press on touch; while something is selected a click on a file toggles it. Keyboard: Tab reaches each file and its buttons, Up / Down (and Left / Right in the grid and chips) move between files, Home / End jump, Page Up / Page Down move by 10, Enter previews, Space toggles selection, Shift + Space or Shift + arrows extend it, Ctrl / Cmd + A selects all, Escape clears, Delete or Backspace deletes the focused file (with the confirm), Shift + F10 or the Menu key opens its menu (from the file or any of its buttons). Files enter and leave on a soft spring; reduced motion turns movement into fades.
File List is a Pro React component in the React data display components category, written in TypeScript for React 19 and Tailwind CSS v4. It installs as source with npx wingo-ui@latest add file-list, together with Alert Dialog, Avatar, Button, Checkbox, Dropdown Menu and 11 more and the npm packages lucide-react and motion.
Add to a project
Requirements: React 19 and Tailwind CSS v4, in Next.js (App Router or Pages), Vite or React Router. Installation guide · Next.js setup · Vite setup
npx wingo-ui@latest add file-listFile List is part of Pro, with 298 more Pro items: $8 a month or $150 once. 14-day refund before you download any Pro item. See plans
Usage
Import FileList from @/components/ui/file-list.
import { useState } from "react"
import { FileList, type FileItem } from "@/components/ui/file-list"
const initial: FileItem[] = [
{ id: "f1", name: "Invoice 2026-0412.pdf", size: 253952, url: "/files/invoice-2026-0412.pdf", uploadedAt: "2026-09-22T11:20:00", uploadedBy: { name: "Olivia Bennett" } },
{ id: "f2", name: "Sales report Q3 2026.xlsx", size: 1258291, url: "/files/report-q3.xlsx", uploadedAt: "2026-09-24T10:12:00", uploadedBy: { name: "Liam Walker" } },
{ id: "f3", name: "Admin ID scan.pdf", size: 3984589, status: "uploading", progress: 0.64 },
]
export function ClientDocuments() {
const [files, setFiles] = useState(initial)
const remove = (file: FileItem) => setFiles((prev) => prev.filter((f) => f.id !== file.id))
return (
<FileList
files={files}
locale="en-US"
onDelete={async (file) => {
await fetch(`/api/files/${file.id}`, { method: "DELETE" })
remove(file)
}}
onCancel={remove}
getActions={(file) => [{ label: "Copy link", onSelect: () => navigator.clipboard.writeText(file.url ?? "") }]}
labels={{ deleteTitle: (name) => `Delete “${name}” for everyone?` }}
/>
)
}Props
filesFileItem[]{ id, name, size?, type?, url?, thumbnailUrl?, previewUrl?, uploadedAt?, uploadedBy?: { name, avatar? }, status?: "done" | "uploading" | "processing" | "error" | "idle" | "canceled", progress? (0..1), error?, description?, disabled? }. FileUpload's UploadFile[] fits as it is. To render one file on its own, use FileListItem (see Parts). Unknown sizes are left out of the meta line; heic and tiff never try to render.
layoutdefault"list""list" | "grid" | "compact"list: rows with a thumb, name, meta and actions. grid: preview tiles (4:3 photos or a big type tile) in auto-fill columns of at least 10rem, 2 columns on a phone. compact: 40px chips that wrap, for email attachments.
sizedefault"md""sm" | "md"List rows 44 / 56px with 32 / 40px thumbs and a 13 / 14px name.
variantdefault"surface""plain" | "surface" | "separated"surface: one raised panel with hairlines (raised tiles and chips in the other layouts); plain: rows with hairlines and no container (flat gray tiles, tinted chips); separated: a panel per file (outlined tiles and chips).
radiusdefault"md""none" | "sm" | "md" | "lg"Panel and tile corners: 0 / 12 / 16 / 24px; previews inside a tile follow at the radius minus the padding. Chips are pills from md up.
tonedefault"neutral""neutral" | "brand" | "success" | "warning" | "danger" | "info"The selection checkbox, the selected ring on tiles and chips, and the upload progress fill.
colordefault""stringAny CSS color; replaces the tone. Leave empty to use the tone.
showThumbnailsdefaulttruebooleanPhoto previews for images (thumbnailUrl, then previewUrl, then url). Off shows the colored type tile for every file.
showMetadefaulttruebooleanThe size · time · uploader line under the name. Upload progress and errors show either way.
timeFormatdefault"relative""relative" | "time" | "date" | "datetime"How uploadedAt reads: "2 days ago", "4:40 PM", "9/22/2026" or both. Relative text waits for the browser, so the server prints the date and time.
actionsVisibilitydefault"hover""hover" | "always"The ⋯ button: shown on hover and focus (always on touch screens), or always.
confirmDeletedefaulttruebooleanDelete asks first in an alert dialog. Off deletes right away (email attachments).
selectionModedefault"none""none" | "multiple"multiple adds a checkbox per finished file, Shift ranges, Ctrl / Cmd + A and Escape, and long press to select on touch. Uploading and failed files cannot be selected.
loadingdefaultfalsebooleanSkeletons in the shape of the layout (rows, tiles or chips) instead of the files, with aria-busy.
skeletonCountdefault3numberHow many skeletons show while loading.
localedefault""stringSizes ("2.4 MB") and times ("2 days ago"). Empty uses the UIProvider locale, then the browser's. The demo falls back to en-US.
onPreview(file: FileItem, index: number) => voidClicking a file or pressing Enter on it. Without it a file with a url opens in a new tab.
onDownload(file: FileItem) => voidThe download action. Without it the download button is a link to the url with the download attribute.
onDelete(file: FileItem) => (void | Promise<unknown>)Shows Delete in the ⋯ menu (and the Delete key). A returned promise fades the row to 50% until it settles; afterwards focus moves to the next file. Also removes a failed upload (without asking).
onCancel(file: FileItem) => voidThe × on uploading, waiting and processing files; also removes a failed upload when there is no onDelete.
onRetry(file: FileItem) => voidRetry on failed uploads.
getActions(file: FileItem) => MenuEntry[]Extra ⋯ entries in the W1 MenuEntry shape (Rename, Move, Copy link), above Delete. In compact chips it turns the trailing button into a ⋯.
selectedKeysstring[]The selected file ids (controlled). Pair with onSelectedKeysChange.
defaultSelectedKeysdefault"[]"string[]The initial selection when uncontrolled.
onSelectedKeysChange(keys: string[]) => voidEvery selection change, in display order.
emptyStateReactNodeShown when files is empty. Defaults to <EmptyState size="sm" preset="empty" icon={<Paperclip />} /> with labels.empty.
labelsPartial<FileListLabels>Every string, merged over DEFAULT_FILE_LIST_LABELS: preview, download, delete, cancel, retry, remove, actions and select ({name}), uploading ({ percent, loaded, total }) => string, processing, queued, canceled, failed, uploaded, deleteTitle (name) => string, deleteDescription, deleteConfirm, deleteCancel, sheetTitle, sheetCancel, empty, emptyDescription, loading, kinds (screen reader names per file kind).
classNamesPartial<Record<"item" | "main" | "select" | "thumb" | "body" | "name" | "meta" | "description" | "progress" | "error" | "actions" | "empty" | "skeleton", string>>Extra classes for the inner parts.
classNamestringExtra classes for the root (the panel, grid or chip row).
Parts
FileListItem
componentOne file as a row, a tile or a chip, following the FileList around it; on its own it is a plain row that opens its url. Takes every div prop plus the ones below.
file*FileItemThe file to render (see FileItem).
indexdefault0numberThe position handed to onPreview.
DEFAULT_FILE_LIST_LABELS
constantEvery English string: the file actions (Preview, Download, Delete, Cancel upload, Retry, Remove), the "{name}" templates for the menu and selection, upload states ("42% · 1.2 MB of 3 MB"), the delete dialog, the action sheet and the empty and loading states. Spread it into labels to translate a few. Type: FileListLabels.
FileItem
typeOne file: { id, name, size?, type?, url?, thumbnailUrl?, previewUrl?, uploadedAt?, uploadedBy?, status?, progress?, error?, description?, disabled? }. status is "done", "uploading", "processing", "error", "idle" or "canceled"; progress runs from 0 to 1.
Examples
Grid with photo previews
layout="grid": listing photos fill 4:3 tiles, documents show their kind tile; 2 columns on a phone.
Email attachments
layout="compact": wrapping chips under a composer, with a total and a remove button.
Selection with a bulk bar
selectionMode="multiple" with a select-all checkbox, Shift ranges and bulk actions.
Upload queue
FileUpload picks the files, FileList shows progress, cancel, a failure and retry.
Loading and empty
Skeletons in the row shape while loading, then the files or an EmptyState.
Narrow sidebar
size="sm" plain rows in a 300px card: 44px rows with 32px thumbs.
Separated, brand selection
A panel per file with a description line, brand checkboxes and the ⋯ always visible.
Dependencies
Files
- components/ui/file-list.tsx
FAQ
Is the React File List component free?
The preview, props and docs are free; copying the source needs a Pro plan. See plans.
How do I install File List?
Run npx wingo-ui@latest add file-list. It also adds the Wingo UI items it builds on (Alert Dialog, Avatar, Button, Checkbox, Dropdown Menu and 11 more) and the npm packages lucide-react and motion. With shadcn, add the @wingo-ui registry with your API key, then run npx shadcn@latest add @wingo-ui/file-list.
How does File List behave on phones?
When the list is narrower than 448px (a phone, a sidebar card) download moves into the ⋯ menu and uploaders show as a face only; on a phone the menu opens as a bottom sheet, and a long press opens it too.
See also
Used in
More React data display components
Articles using this component
- React File Upload: Drag and Drop, Paste, Camera, Progress
Component guides11 min read


