Skip to content

FileExplorer

FileExplorer is a generic primitive. Hosts pass an items array mapped to ExplorerItem, optional actions, an onOpen, and (for editable lists) an onRename. The explorer owns selection, view mode, sort, search, rename, and the right-click context menu.

Open Recents from the dock, try multi-select Open in new tab ↗
import {
FileExplorer,
type ExplorerItem,
type ExplorerAction,
type ExplorerSidebarSection,
} from "@react-ui-os/desktop";
const items: ExplorerItem[] = downloads.map((d) => ({
id: d.id,
name: d.filename,
kind: d.format,
timestamp: d.createdAt,
icon: <FileIcon />,
}));
<FileExplorer
items={items}
onOpen={(item) => reopen(item.id)}
onRename={(item, newName) => rename(item.id, newName)}
actions={[
{
id: "delete",
label: "Delete",
shortcut: "",
danger: true,
onClick: (selected) => selected.forEach((s) => del(s.id)),
},
]}
/>;
PropTypeDefaultDescription
itemsT[] (extends ExplorerItem)requiredThe list to render. Order is preserved; sort + filter are applied on top.
onOpen(item: T) => voidundefinedCalled on Enter or double-click. Receives the single item.
onRename(item: T, newName: string) => voidundefinedEnables F2 + context-menu Rename. Receives the new name after Enter or blur.
actionsExplorerAction<T>[][]Toolbar + context-menu actions. See Actions below.
sidebarExplorerSidebarSection[]undefinedOptional Finder-style left rail. Hidden when not provided.
emptyStateReactNode"Nothing here yet."Shown when items.length === 0.
defaultView"icons" | "list""icons"Initial view mode. The toolbar toggle is always available.
interface ExplorerItem {
id: string;
name: string;
kind?: string; // "Kind" cell in list view + sort field
timestamp?: number; // epoch ms, drives the Date column + sort
subtitle?: string; // optional one-line subtitle in icon view
meta?: string; // optional right-aligned metadata in list view
icon?: ReactNode; // tile icon in grid view
iconSmall?: ReactNode; // compact icon in list view (defaults to `icon`)
}
interface ExplorerAction<T extends ExplorerItem = ExplorerItem> {
id: string;
label: string;
icon?: ReactNode;
onClick: (items: T[]) => void;
danger?: boolean; // red label + divider in context menu
singleOnly?: boolean; // hidden when >1 items selected
shortcut?: string; // keyboard hint in the context menu ("⌫", "F2")
}
  • onClick receives the full current selection. Actions are bulk-safe by default; mark single-item actions with singleOnly: true.
  • Actions with id: "delete" are auto-bound to the Delete / Backspace key.
  • danger: true rows render in red and sit below a divider in the context menu.
interface ExplorerSidebarSection {
label: string;
items: ExplorerSidebarItem[];
}
interface ExplorerSidebarItem {
id: string;
label: string;
icon?: ReactNode;
iconColor?: string;
active?: boolean;
onClick: () => void;
}

Used to mirror Finder’s “Favorites” rail. Hosts decide what activation does (route navigation, opening a different folder window, etc.).

The interaction model matches macOS Finder; drift from this only with a good reason.

Gesture / keyEffect
Click an itemReplaces the selection
Cmd / Ctrl + clickToggles the item in/out
Shift + clickRange-selects from the anchor
Click on empty spaceClears the selection
Double-clickonOpen(item)
Right-click on an itemPromotes (if not already selected), opens item context menu
Right-click on empty spaceOpens “View as” / “Sort by” context menu
EnterOpens the single selected item
F2Begins rename (stem pre-selected; Enter / blur commits, Esc cancels)
Delete / BackspaceInvokes the action with id: "delete" if present
Cmd / Ctrl + ASelects all filtered items
EscapeClears selection, closes menus, cancels rename

When items changes (e.g. after a delete), the explorer drops any selected ids that no longer exist. Hosts don’t need to manage that.