File List Columns
A plain list as used by Finder's column view, with focused and unfocused selection.
@cupertinoui-pro/file-list-columnsCupertinoUI Pro"use client"
import * as React from "react"
import type { FileNode } from "@/registry/macos/lib/file-tree"
import {
Dialog,
DialogContent,
DialogDescription,
DialogHeader,
DialogTitle,
} from "@/components/ui/dialog"
import { FileListColumns } from "@/components/ui/file-list-columns"
// The same JSON as the File List Table demo: folders have children.
const initialFiles: FileNode[] = [
{
name: "Design",
modified: "Today at 09:41",
children: [
{ name: "Icons.sketch", kind: "Sketch Document", modified: "Today at 09:41", size: "18,2 MB" },
{ name: "Wallpaper.heic", kind: "HEIF Image", modified: "Today at 09:12", size: "4,1 MB" },
],
},
{
name: "Invoices",
modified: "Yesterday at 17:02",
children: [
{
name: "2026",
modified: "Yesterday at 17:02",
children: [
{ name: "September.pdf", kind: "PDF Document", modified: "1. Oct 2026 at 08:30", size: "212 KB" },
{ name: "October.pdf", kind: "PDF Document", created: "Yesterday at 16:58", modified: "Yesterday at 17:02", size: "198 KB" },
],
},
],
},
{ name: "Notes.txt", kind: "Plain Text", modified: "12. Oct 2026 at 12:29", size: "4 KB" },
{ name: "Roadmap.key", kind: "Keynote", modified: "8. Oct 2026 at 10:15", size: "2,4 MB" },
]
// Click a folder to open it in the next column, or use the arrow keys; a
// selected file shows its preview after the last column.
// Drag over items to select several, drag selected items onto a folder or
// into another column to move them, press ⌘⌫ to delete them, and
// double-click a file to open it in a window.
export default function FileListColumnsDemo() {
const [files, setFiles] = React.useState(initialFiles)
const [opened, setOpened] = React.useState<FileNode | null>(null)
return (
<div className="flex h-[480px] w-full max-w-3xl overflow-hidden rounded-[12px] bg-mac-content shadow-[0_0_0_1px_var(--mac-grid)]">
<FileListColumns
files={files}
onFilesChange={setFiles}
onOpen={setOpened}
defaultSelected={["Invoices/2026/October.pdf"]}
columnWidth={180}
className="flex-1"
/>
<Dialog open={opened !== null} onOpenChange={(open) => !open && setOpened(null)}>
<DialogContent className="sm:max-w-xs">
<DialogHeader>
<DialogTitle>{opened?.name}</DialogTitle>
<DialogDescription>
{[opened?.kind, opened?.size].filter(Boolean).join(" - ")}
</DialogDescription>
</DialogHeader>
</DialogContent>
</Dialog>
</div>
)
}Installation
pnpm dlx shadcn@latest add @cupertinoui-pro/file-list-columnsPart of CupertinoUI Pro: the CLI installs it with your license key, once the @cupertinoui-pro registry is in components.json.
Usage
import { FileListColumns } from "@/components/ui/file-list-columns"
import type { FileNode } from "@/lib/file-tree"
const files: FileNode[] = [
{
name: "Documents",
children: [{ name: "Notes.txt", kind: "Plain Text", size: "4 KB" }],
},
]
<FileListColumns files={files} defaultSelected={["Documents/Notes.txt"]} />files is plain JSON, the same structure FileListTable takes, so both can show one tree. Selecting a folder opens its contents in a new column; the arrow keys move up and down a column and in and out of folders.
Like Finder, drag over items to select several in one column, from an item or from empty space; Shift-click selects a range and ⌘-click adds or removes an item. With several selected, no further column or preview opens.
With onFilesChange, drag selected items onto a folder, or into another column, to move them there; the browser follows them. Escape cancels, and ⌘⌫ deletes the selection.
Right-click for Finder's menu: Open, Move to Trash, Rename, Duplicate, Copy and the tag colors; on empty space, New Folder. Renaming edits the name in place (Return also starts it), ⌘D duplicates and ⇧⌘N makes a folder. Without onFilesChange the menu only opens and copies.
Expanded folders and the selection are keys: the ids from the top of the tree down to the file, joined by "/", e.g. "Invoices/2026/October.pdf".
Columns open 244px wide, like Finder's; set columnWidth to change that. Drag a column's right edge to resize it. When the columns don't fit, the view scrolls and keeps the last column in view.
A selected file shows Finder's preview pane after the last column (FileListColumnsPreview): its thumbnail, or a large document icon labeled with its extension (with a zipper for archives), then its name, kind and size, Created, Modified and info rows, and tags. The picture shrinks with the pane's height and stays centered above the name. Pass renderPreview to show something else, or compose FileListColumnsPreview with your own artwork and actions.
Examples
Composition
Without files, FileListColumns is a single column that you fill with items, and you decide what the next column shows.
"use client"
import * as React from "react"
import { FileTextIcon, FolderIcon } from "lucide-react"
import { FileListColumns, FileListColumnsItem, FileListColumnsItemChevron, FileListColumnsItemLabel } from "@/components/ui/file-list-columns"
const folders: Record<string, string[]> = {
Projects: ["website", "mobile-app", "design-system"],
Archive: ["2024", "2025"],
Photos: ["Iceland", "Lisbon"],
}
// Two columns of a Finder-style column view. The focused column is blue.
export default function FileListColumnsComposed() {
const [folder, setFolder] = React.useState("Projects")
const [file, setFile] = React.useState("website")
const [focus, setFocus] = React.useState<"left" | "right">("right")
return (
<div className="flex h-56 w-full max-w-lg overflow-hidden rounded-[12px] bg-mac-content shadow-[0_0_0_1px_var(--mac-grid)]">
<FileListColumns active={focus === "left"} className="w-1/2 border-r border-mac-grid">
{Object.keys(folders).map((name) => (
<FileListColumnsItem
key={name}
selected={folder === name}
onClick={() => {
setFolder(name)
setFile(folders[name][0])
setFocus("left")
}}
>
<FolderIcon className="fill-mac-blue text-mac-blue" />
<FileListColumnsItemLabel>{name}</FileListColumnsItemLabel>
<FileListColumnsItemChevron />
</FileListColumnsItem>
))}
</FileListColumns>
<FileListColumns active={focus === "right"} className="w-1/2">
{folders[folder].map((name) => (
<FileListColumnsItem
key={name}
selected={file === name}
onClick={() => {
setFile(name)
setFocus("right")
}}
>
<FileTextIcon className="text-mac-label-secondary" />
<FileListColumnsItemLabel>{name}</FileListColumnsItemLabel>
</FileListColumnsItem>
))}
</FileListColumns>
</div>
)
}API Reference
Every part forwards the props of the underlying element. The props below are the ones CupertinoUI adds or changes.
FileNode
One file or folder in files: plain JSON, the same for every file list. Import the type from @/lib/file-tree.
| Prop | Type | Description |
|---|---|---|
name | string | The file name. |
id | string | Unique among its siblings, for keys; defaults to the name. Can't contain "/". |
children | FileNode[] | Makes it a folder; folders nest to any depth. Use [] for an empty folder. |
thumbnail | string | An image URL shown instead of the icon in the icon and gallery views, and as the preview. |
kind / size / created / modified | string | Shown as they are, so format them the way you want them displayed. |
info | Record<string, string> | More Information rows for the column and gallery previews, e.g. { Dimensions: "2007×2853" }. |
icon | string | A hint for renderIcon, e.g. "image" or "movie". |
tag | string | A Finder tag color: "red", "orange", "yellow", "green", "blue", "purple" or "gray". |
onOpen | (file: FileNode) => void | Called when this file or folder is opened: double-clicked, or ⌘O. |
FileListColumns
| Prop | Type | Description |
|---|---|---|
files | FileNode[] | The tree to browse. Leave it out to use FileListColumns as a single column. |
multiple | booleanDefault: true | Lets people select several items in a column: drag over them, Shift-click a range, ⌘-click single items, Shift with the arrow keys or ⌘A. false allows one item. |
defaultSelected | string[] | The keys of the files that start selected, all in one folder; the folders above them are open. |
selected | string[] | The keys of the selected files, controlled. Update it from onSelectedChange. |
onSelectedChange | (keys: string[], files: FileNode[]) => void | Called when the selection changes, with the keys and files in order. |
renderIcon | (file: FileNode) => ReactNode | The 16px icon of a file. Defaults to a folder or a document. |
renderPreview | (file: FileNode) => ReactNode | Shown after the last column when one file is selected, in place of FileListColumnsPreview. |
columnWidth | numberDefault: 244 | The width columns open at, in pixels. Columns can then be resized by dragging their right edge. |
onFilesChange | (files: FileNode[], change: FileChange) => void | Turns on editing: dragging selected items onto a folder moves them, ⌘⌫ deletes them. Gets the new tree to store, and what changed: { action: "move", keys, to } or { action: "delete", keys }. |
onOpen | (file: FileNode, key: string) => void | Called when a file is opened: double-clicked, or ⌘O or ⌘↓ on the selection. Use it to show the file, e.g. in a new window. |
menuItems | (files: FileNode[], keys: string[]) => ReactNode | More ContextMenuItems for the right-click menu, after Finder's own. |
active | booleanDefault: false | Without files: the column with keyboard focus, whose selection is blue; others are grey. |
className | string | Extra classes, merged with the component's own. |
FileListColumnsItem
| Prop | Type | Description |
|---|---|---|
selected | booleanDefault: false | Marks the selected item. |
render | ReactElement | Render a link or button with the item's styles. |
FileListColumnsItemLabel / FileListColumnsItemChevron
| Prop | Type | Description |
|---|---|---|
className | string | Extra classes, merged with the component's own. |
FileListColumnsPreview
The preview pane: the default for a selected file.
| Prop | Type | Description |
|---|---|---|
file | FileNode | The file to preview. |
artwork | ReactNode | Replaces the picture. Defaults to the thumbnail, or a large document icon. |
actions | ReactNode | Quick actions at the bottom, as FileListColumnsPreviewActions. |
className | string | Extra classes, merged with the component's own. |
FileListColumnsPreviewAction
A quick action under the preview: an icon over a label, as a button.
| Prop | Type | Description |
|---|---|---|
className | string | Extra classes, merged with the component's own. |