File trees
A file tree with declarative folders and one selection
selected: src/index.ts
Usage
import {
FileTree,
FileTreeFile,
FileTreeFolder,
} from "@gpuixui/files";
const [file, setFile] = useState("src/index.ts");
<FileTree
aria-label="Project files"
defaultExpanded={["src"]}
height={280}
selected={file}
onSelect={setFile}
>
<FileTreeFolder path="src" name="src">
<FileTreeFolder path="src/components" name="components">
<FileTreeFile path="src/components/button.tsx" name="button.tsx" />
</FileTreeFolder>
<FileTreeFile path="src/index.ts" name="index.ts" />
</FileTreeFolder>
<FileTreeFile path="readme.md" name="readme.md" />
</FileTree>;A folder press toggles only that folder's subtree. A file press selects it
and calls onSelect once. The tree holds one tab stop — the selected file while
it paints, else the first row — and the arrow keys walk the visible rows, with
Right opening and Left closing a folder.
The selected file wears the accent wash with the accent's foreground, as
the reference's bg-accent text-accent-foreground does: a row is one of many,
so a brand fill would read as a pressed primary button.
FileTreeFolder and FileTreeFile declare the tree; FileTree reads their
props into the same FileNode model buildFileTree produces and paints one
flat row list. The declarations are not rendered recursively, which is what
lets the generic tree keep its single tab stop and its visible-row order.
API
| Prop | Type | Default | Consequence |
|---|---|---|---|
expanded | Iterable<string> | — | the open folder paths, when the caller holds them |
defaultExpanded | Iterable<string> | none | the open paths at first paint; a saved JSON list works |
selected | string | null | none | the selected file path; a non-text stored value paints no selection |
onSelect | (path: string) => void | — | a file press or Enter, called once per activation |
onExpandedChange | (expanded: string[]) => void | — | the open paths after every change, sorted |
height | number | — | a definite height; the rows scroll inside it |
className | string | — | merged after the frame's own recipe |
testId | string | — | the tree; each row is ${testId}-row-${path} |
Parts
FileTreeFolder
| Prop | Type | Default | Consequence |
|---|---|---|---|
path | string | — | the folder's stable path and expansion key |
name | string | — | the row's visible and accessible name |
children | ReactNode | — | the folder's entries, in paint order |
FileTreeFile
| Prop | Type | Default | Consequence |
|---|---|---|---|
path | string | — | the file's stable path and what onSelect reports |
name | string | — | the row's visible and accessible name |
icon | IconName | the kind's glyph | an icon that overrides the file's kind |
children | ReactNode | the default row | a custom row after the guides and chevron slot |
A custom row is how a file carries actions or an inline editor: the caller
supplies its own FileTreeIcon, FileTreeName and controls, exactly as the
reference's FileTreeActions sits beside its button.
FileTreeIcon and FileTreeName
The two row slots the default row composes. FileTreeIcon takes name
(IconName), selected and a children fallback; FileTreeName takes
selected and the name. Both paint in the selected row's ink when selected is
set, so a custom row can match the tree.
Reference anatomy
| shadcn AI Elements | GPUixUI |
|---|---|
FileTree (role="tree", frame, expanded / defaultExpanded / selectedPath / onSelect) | FileTree (role="tree", frame, expanded / defaultExpanded / selected / onSelect) |
FileTreeFolder (Collapsible, trigger, FileTreeIcon, FileTreeName, nested lane) | FileTreeFolder (one row, chevron glyph, folder glyph, name, inset by depth) |
FileTreeFile (Button, spacer, icon, name) | FileTreeFile (one row, blank chevron slot, file glyph, name) |
FileTreeIcon (shrink-0 span) | FileTreeIcon (icon-sized slot) |
FileTreeName (truncate span) | FileTreeName (one ellipsized line) |
FileTreeActions (right-aligned, stops the row press) | omitted: a FileTreeFile custom row is the action slot |
A row is the theme's label type role — the reference's text-sm in the sans
family — and every row is the density's rowHeight, rounded one step under the
radius. The tree paints no frame of its own: no border, no background, no
guide rails, so the surface it sits on (a card, a sidebar, a sheet) is the only
one. One folder glyph and one file glyph cover every row; the chevron says
whether a folder is open, and the kind a name classifies into is a fact an app
can ask for without the paint following it.
Edit this page on GitHubpackages/files/docs/file-tree.mdx