# File List

> A React file list for attachments and documents: rows, preview tiles or chips with type icons, thumbnails, upload progress and file actions.

- Type: React component (Data Display)
- Access: Pro (the source needs a Wingo UI Pro plan, see https://wingo-ui.com/pricing)
- Version: 1.0.0
- Install: `npx wingo-ui@latest add file-list`
- Docs: https://wingo-ui.com/components/file-list

## Interaction

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.

## Installation

```bash
npx wingo-ui@latest add file-list
```

Pro item: run `npx wingo-ui@latest login` once with a Pro account. The CLI adds the files, their dependencies and a version record for updates.

With shadcn: `npx shadcn@latest add @wingo-ui/file-list` (needs the @wingo-ui registry with your API key in components.json)

Files:

- `components/ui/file-list.tsx`

npm dependencies: `npm i lucide-react motion`

Wingo UI dependencies (installed automatically):

- [Alert Dialog](https://wingo-ui.com/components/alert-dialog) ([Markdown](https://wingo-ui.com/components/alert-dialog.md))
- [Avatar](https://wingo-ui.com/components/avatar) ([Markdown](https://wingo-ui.com/components/avatar.md))
- [Button](https://wingo-ui.com/components/button) ([Markdown](https://wingo-ui.com/components/button.md))
- [Checkbox](https://wingo-ui.com/components/checkbox) ([Markdown](https://wingo-ui.com/components/checkbox.md))
- [Dropdown Menu](https://wingo-ui.com/components/dropdown-menu) ([Markdown](https://wingo-ui.com/components/dropdown-menu.md))
- [Empty State](https://wingo-ui.com/components/empty-state) ([Markdown](https://wingo-ui.com/components/empty-state.md))
- [File types](https://wingo-ui.com/components/file-types) ([Markdown](https://wingo-ui.com/components/file-types.md))
- [Motion presets](https://wingo-ui.com/components/motion) ([Markdown](https://wingo-ui.com/components/motion.md))
- [Progress](https://wingo-ui.com/components/progress) ([Markdown](https://wingo-ui.com/components/progress.md))
- [Relative time](https://wingo-ui.com/components/relative-time) ([Markdown](https://wingo-ui.com/components/relative-time.md))
- [Skeleton](https://wingo-ui.com/components/skeleton) ([Markdown](https://wingo-ui.com/components/skeleton.md))
- [Tones](https://wingo-ui.com/components/tones) ([Markdown](https://wingo-ui.com/components/tones.md))
- [UIProvider](https://wingo-ui.com/components/ui-config) ([Markdown](https://wingo-ui.com/components/ui-config.md))
- [useLocale](https://wingo-ui.com/components/use-locale) ([Markdown](https://wingo-ui.com/components/use-locale.md))
- [useLongPress](https://wingo-ui.com/components/use-long-press) ([Markdown](https://wingo-ui.com/components/use-long-press.md))
- [useSelection](https://wingo-ui.com/components/use-selection) ([Markdown](https://wingo-ui.com/components/use-selection.md))

## Usage

```tsx
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

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `files?` | `FileItem[]` |  | { 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. |
| `layout?` | `"list" \| "grid" \| "compact"` | `"list"` | 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. |
| `size?` | `"sm" \| "md"` | `"md"` | List rows 44 / 56px with 32 / 40px thumbs and a 13 / 14px name. |
| `variant?` | `"plain" \| "surface" \| "separated"` | `"surface"` | 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). |
| `radius?` | `"none" \| "sm" \| "md" \| "lg"` | `"md"` | 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. |
| `tone?` | `"neutral" \| "brand" \| "success" \| "warning" \| "danger" \| "info"` | `"neutral"` | The selection checkbox, the selected ring on tiles and chips, and the upload progress fill. |
| `color?` | `string` | `""` | Any CSS color; replaces the tone. Leave empty to use the tone. |
| `showThumbnails?` | `boolean` | `true` | Photo previews for images (thumbnailUrl, then previewUrl, then url). Off shows the colored type tile for every file. |
| `showMeta?` | `boolean` | `true` | The size · time · uploader line under the name. Upload progress and errors show either way. |
| `timeFormat?` | `"relative" \| "time" \| "date" \| "datetime"` | `"relative"` | 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. |
| `actionsVisibility?` | `"hover" \| "always"` | `"hover"` | The ⋯ button: shown on hover and focus (always on touch screens), or always. |
| `confirmDelete?` | `boolean` | `true` | Delete asks first in an alert dialog. Off deletes right away (email attachments). |
| `selectionMode?` | `"none" \| "multiple"` | `"none"` | 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. |
| `loading?` | `boolean` | `false` | Skeletons in the shape of the layout (rows, tiles or chips) instead of the files, with aria-busy. |
| `skeletonCount?` | `number` | `3` | How many skeletons show while loading. |
| `locale?` | `string` | `""` | Sizes ("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) => void` |  | Clicking a file or pressing Enter on it. Without it a file with a url opens in a new tab. |
| `onDownload?` | `(file: FileItem) => void` |  | The 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) => void` |  | The × on uploading, waiting and processing files; also removes a failed upload when there is no onDelete. |
| `onRetry?` | `(file: FileItem) => void` |  | Retry 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 ⋯. |
| `selectedKeys?` | `string[]` |  | The selected file ids (controlled). Pair with onSelectedKeysChange. |
| `defaultSelectedKeys?` | `string[]` | `"[]"` | The initial selection when uncontrolled. |
| `onSelectedKeysChange?` | `(keys: string[]) => void` |  | Every selection change, in display order. |
| `emptyState?` | `ReactNode` |  | Shown when files is empty. Defaults to <EmptyState size="sm" preset="empty" icon={<Paperclip />} /> with labels.empty. |
| `labels?` | `Partial<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). |
| `classNames?` | `Partial<Record<"item" \| "main" \| "select" \| "thumb" \| "body" \| "name" \| "meta" \| "description" \| "progress" \| "error" \| "actions" \| "empty" \| "skeleton", string>>` |  | Extra classes for the inner parts. |
| `className?` | `string` |  | Extra classes for the root (the panel, grid or chip row). |

## Parts

### FileListItem (component)

One 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.

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `file` | `FileItem` |  | The file to render (see FileItem). |
| `index?` | `number` | `0` | The position handed to onPreview. |

### DEFAULT_FILE_LIST_LABELS (constant)

Every 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 (type)

One 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.

## See also

- [File Upload](https://wingo-ui.com/components/file-upload) ([Markdown](https://wingo-ui.com/components/file-upload.md))
- [File types](https://wingo-ui.com/components/file-types) ([Markdown](https://wingo-ui.com/components/file-types.md))
- [List](https://wingo-ui.com/components/list) ([Markdown](https://wingo-ui.com/components/list.md))
- [Progress](https://wingo-ui.com/components/progress) ([Markdown](https://wingo-ui.com/components/progress.md))
- [Empty State](https://wingo-ui.com/components/empty-state) ([Markdown](https://wingo-ui.com/components/empty-state.md))

---

Canonical: https://wingo-ui.com/components/file-list
More components: https://wingo-ui.com/components · Index for agents: https://wingo-ui.com/llms.txt
