# Files

> A complete screen built from Chip, CommandPalette, FileCard, IconButton and 6 more components.

Pattern · Pro kit · [Live page](https://ui.twentyfirst.media/pro/patterns/FilesScreen/)

## Code

```tsx
import * as React from "react";
import {
  Chip,
  CommandPalette,
  FileCard,
  PageHeader,
  SearchField,
  Stagger,
  Tabs,
} from "@twentyfirstmedia/pro";
import { Shell } from "./_workspace";
import type { FileKind } from "@twentyfirstmedia/pro";

const F: [string, FileKind, string, string, boolean?][] = [
  ["Northwind – master agreement.pdf", "pdf", "412 KB", "Today", true],
  ["Kestrel Bank – proposal v3.pdf", "pdf", "1.8 MB", "Yesterday", true],
  ["DPA template (DSGVO).doc", "doc", "96 KB", "Mon"],
  ["pricing-tiers.fig", "fig", "18 MB", "Today", true],
  ["dashboard-shots.png", "img", "2.4 MB", "Yesterday"],
  ["brand-assets.zip", "zip", "36 MB", "Apr 2"],
  ["support-macros.sheet", "sheet", "58 KB", "Yesterday"],
  ["pnl-q1-2026.sheet", "sheet", "92 KB", "Apr 1", true],
];
function App() {
  return (
    <Shell
      area="Files"
      current="files"
      action={{
        label: "Upload",
        icon: "paperclip",
      }}
      pages={[
        {
          id: "files",
          label: "All files",
          icon: "folder",
        },
      ]}
      overlay={
        <CommandPalette
          inline
          onClose={() => {}}
          items={[
            {
              id: 1,
              group: "Pages",
              label: "All files",
              hint: "Files",
              icon: "folder",
            },
            {
              id: 2,
              group: "Actions",
              label: "Upload files",
              icon: "paperclip",
              tint: "butter",
            },
            {
              id: 3,
              group: "Actions",
              label: "New invoice",
              icon: "card",
              tint: "butter",
            },
            {
              id: 4,
              group: "Actions",
              label: "Compose mail",
              icon: "edit",
              tint: "sky",
            },
            {
              id: 5,
              group: "Actions",
              label: "New agent",
              icon: "bot",
              tint: "lilac",
            },
          ]}
        />
      }
    >
      <div className="tf-in">
        <PageHeader
          title="Files"
          subtitle="15 files · drop files anywhere to upload"
          actions={[
            <SearchField key="s" placeholder="Search files" />,
            <Tabs key="t" items={["Grid", "List"]} />,
          ]}
        />
      </div>
      <div
        className="tf-row tf-in"
        style={
          {
            "--d": "80ms",
          } as React.CSSProperties
        }
      >
        {["All", "Contracts", "Receipts", "Design", "Agent knowledge", "Exports"].map((t, i) => (
          <Chip key={t} icon={i ? "folder" : undefined} selected={i === 0}>
            {t}
          </Chip>
        ))}
      </div>
      <Stagger
        className="tf-grid"
        style={{
          gridTemplateColumns: "repeat(auto-fill, minmax(180px, 1fr))",
        }}
        delay={140}
      >
        {F.map((f) => (
          <FileCard key={f[0]} name={f[0]} kind={f[1]} size={f[2]} meta={f[3]} shared={f[4]} />
        ))}
      </Stagger>
    </Shell>
  );
}
export default function Preview() {
  return <App />;
}
```

## Props

### Chip

| Prop | Type | Notes |
| --- | --- | --- |
| `selected?` | `boolean` |  |
| `icon?` | `IconName` |  |
| `count?` | `number` |  |
| `onClick?` | `(e: MouseEvent<HTMLButtonElement>) => void` |  |
| `children?` | `ReactNode` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### CommandPalette

| Prop | Type | Notes |
| --- | --- | --- |
| `items` | `PaletteItem[]` | Required |
| `onClose?` | `() => void` |  |
| `placeholder?` | `string` |  |
| `inline?` | `boolean` |  |
| `loading?` | `boolean` | Results are on their way (async search): shows a loading row. |
| `empty?` | `ReactNode` | Shown when nothing matches (default: "Nothing matches …"). |
| `onQueryChange?` | `(query: string) => void` | Called with the query as the user types, for searching on a server. |
| `open?` | `boolean` | Optional: with `open` the component stays mounted while it animates out; without it, render it only while open. |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### FileCard

| Prop | Type | Notes |
| --- | --- | --- |
| `name` | `string` | Required |
| `kind` | `FileKind` | Required |
| `size?` | `string` |  |
| `meta?` | `string` |  |
| `shared?` | `boolean` |  |
| `onClick?` | `() => void` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### Icon

| Prop | Type | Notes |
| --- | --- | --- |
| `name` | `IconName` | Required |
| `size?` | `number` |  |
| `label?` | `string` |  |
| `className?` | `string` |  |
| `style?` | `CSSProperties` | Inline style on the root element. |

### IconButton

| Prop | Type | Notes |
| --- | --- | --- |
| `icon` | `IconName` | Required |
| `label` | `string` | Accessible name and tooltip (`aria-label` and `title`); `aria-label` or `title` override them. |
| `variant?` | `"soft" \| "solid" \| "outline" \| "ghost"` |  |
| `size?` | `"sm" \| "md" \| "lg"` |  |
| `type?` | `"button" \| "submit" \| "reset"` |  |
| `onClick?` | `(e: MouseEvent<HTMLButtonElement>) => void` |  |
| `disabled?` | `boolean` |  |
| `asChild?` | `boolean` | Render the single child element (a link) with the button's look; the icon is placed inside it. |
| `children?` | `ReactNode` |  |
| `id?` | `string` |  |
| `name?` | `string` | Form field name and value, sent with the form when this button submits it. |
| `value?` | `string` |  |
| `form?` | `string` | The id of the form this button belongs to, when it sits outside it. |
| `title?` | `string` |  |
| `tabIndex?` | `number` |  |
| `autoFocus?` | `boolean` |  |
| `aria-label?` | `string` |  |
| `aria-labelledby?` | `string` |  |
| `aria-describedby?` | `string` |  |
| `aria-expanded?` | `boolean \| "true" \| "false"` | For disclosure and menu triggers: whether what the button controls is open. |
| `aria-controls?` | `string` |  |
| `aria-haspopup?` | `boolean \| "true" \| "false" \| "menu" \| "listbox" \| "tree" \| "grid" \| "dialog"` |  |
| `aria-pressed?` | `boolean \| "true" \| "false" \| "mixed"` | For toggle buttons. |
| `aria-current?` | `boolean \| "true" \| "false" \| "page" \| "step" \| "location" \| "date" \| "time"` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### PageHeader

| Prop | Type | Notes |
| --- | --- | --- |
| `crumbs?` | `(string \| Crumb)[]` | A breadcrumb above the title (see `Breadcrumb`): labels, or items with `href`/`onClick`. |
| `crumbLinkAs?` | `ElementType` | The element crumb links render as, e.g. Next.js `Link`. |
| `title` | `string` | Required |
| `subtitle?` | `string` |  |
| `actions?` | `ReactNode[]` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### SearchField

| Prop | Type | Notes |
| --- | --- | --- |
| `placeholder?` | `string` |  |
| `label?` | `string` | Accessible name (default: the placeholder, or "Search"). |
| `shortcut?` | `string` | The key hint, e.g. "⌘K" (hidden on touch screens). |
| `bindShortcut?` | `boolean` | ⌘/Ctrl + the hint's last key (K for "⌘K") focuses the field, except while typing elsewhere. |
| `value?` | `string` |  |
| `defaultValue?` | `string` |  |
| `onChange?` | `(e: ChangeEvent<HTMLInputElement>) => void` |  |
| `onKeyDown?` | `(e: KeyboardEvent<HTMLInputElement>) => void` |  |
| `onFocus?` | `(e: FocusEvent<HTMLInputElement>) => void` |  |
| `onBlur?` | `(e: FocusEvent<HTMLInputElement>) => void` |  |
| `id?` | `string` |  |
| `name?` | `string` |  |
| `autoFocus?` | `boolean` |  |
| `inputProps?` | `InputHTMLAttributes<HTMLInputElement>` | For a search with suggestions (combobox): role, aria-controls, aria-activedescendant … |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### Stagger

| Prop | Type | Notes |
| --- | --- | --- |
| `as?` | `string` |  |
| `className?` | `string` |  |
| `style?` | `CSSProperties` |  |
| `delay?` | `number` | Extra delay before the first child, in ms. |
| `reveal?` | `boolean` | Below the fold, wait until the group scrolls into view (default true). |
| `children?` | `ReactNode` |  |

### SubNav

| Prop | Type | Notes |
| --- | --- | --- |
| `eyebrow?` | `string` |  |
| `action?` | `{ label: string; icon?: IconName; onClick?: () => void }` |  |
| `items` | `SubNavItem[]` | Required |
| `current?` | `string` |  |
| `onPick?` | `(id: string) => void` |  |
| `children?` | `ReactNode` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### Tabs

| Prop | Type | Notes |
| --- | --- | --- |
| `items` | `string[]` | Required |
| `value?` | `string` |  |
| `defaultValue?` | `string` |  |
| `onChange?` | `(v: string) => void` |  |
| `block?` | `boolean` |  |
| `panels?` | `Record<string, ReactNode>` | Content per tab; renders the tab panels under the bar, wired with tab/tabpanel roles. |
| `label?` | `string` | Accessible name of the tab list. |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |

### TopBar

| Prop | Type | Notes |
| --- | --- | --- |
| `brand?` | `ReactNode` | The product name, or a node (a logo mark with the name). |
| `tag?` | `string` |  |
| `items` | `NavItem[]` | Required |
| `active?` | `string` |  |
| `user?` | `{ name: string; email?: string }` |  |
| `onNavigate?` | `(label: string) => void` |  |
| `extra?` | `ReactNode` |  |
| `userOpen?` | `boolean` |  |
| `onUser?` | `() => void` |  |
| `className?` | `string` | Extra class on the root element. |
| `style?` | `CSSProperties` | Inline style on the root element. |
