# File Tree

A tree of folders and files with the icons already wired up: the folder icon switches between closed and open, files get a document icon, and folders get a chevron. Built on [Tree View](/docs/tree-view), so every Tree View prop works here too.

Pass plain data (`id`, `name`, `children`) instead of writing a render function. Use `TreeView` directly when you need checkboxes, renaming or a fully custom row.

Group: Data

## Import

```tsx
import { FileTree } from "@indoku/ui"
```

## Anatomy

```text
<FileTree items={[{ id, name, children?, icon? }]} />

node with children (even [])  ->  folder: chevron + folder icon (closed or open) + name
node without children         ->  file:   file icon + name
```

## Examples

### Default icons

Folders open and close, and the icon follows. Folders with an empty `children` array still count as folders.

```tsx
function FileTreeBasic() {
  return <FileTree items={files} label="Project" defaultExpandedValue={["src"]} maxW="xs" />
}
```

### Icons by file type

`fileIcon` accepts a function that receives the node, so you can choose an icon from the name or extension. `folderIcon` and `folderOpenIcon` take plain elements.

```tsx
function FileTreeByExtension() {
  const iconFor = (node: FileTreeNode) => {
    const ext = node.name.split(".").pop()
    if (ext === "ts" || ext === "tsx") return <LuFileCode />
    if (ext === "json") return <LuFileJson />
    if (ext === "md") return <LuFileText />
    if (ext === "png" || ext === "svg") return <LuImage />
    return <LuFile />
  }
  return (
    <FileTree
      items={files}
      defaultExpandedValue={["src", "src/components"]}
      folderIcon={<LuFolder />}
      folderOpenIcon={<LuFolderOpen />}
      fileIcon={iconFor}
      maxW="xs"
    />
  )
}
```

### Icon on a single node

Set `icon` on a node to override the icon for that node, in both the open and closed state.

```tsx
function FileTreeNodeIcon() {
  const items: FileTreeNode[] = [
    { id: "docs", name: "docs", icon: <LuBookOpen />, children: [{ id: "docs/intro.md", name: "intro.md" }] },
    { id: "src", name: "src", children: [{ id: "src/index.ts", name: "index.ts" }] },
  ]
  return <FileTree items={items} defaultExpandedValue={["docs", "src"]} maxW="xs" />
}
```

### Controlled

Use `expandedValue` and `selectedValue` to drive the tree from your own state. Values are node ids.

```tsx
function FileTreeControlled() {
  const [expanded, setExpanded] = useState<string[]>(["src"])
  const [selected, setSelected] = useState<string[]>([])
  const folders = ["src", "src/components", "public", "empty"]
  return (
    <Stack gap="4" maxW="xs">
      <HStack>
        <Button size="xs" variant="outline" onClick={() => setExpanded(folders)}>
          Expand all
        </Button>
        <Button size="xs" variant="outline" onClick={() => setExpanded([])}>
          Collapse all
        </Button>
      </HStack>
      <FileTree
        items={files}
        expandedValue={expanded}
        onExpandedChange={(d) => setExpanded(d.expandedValue)}
        selectedValue={selected}
        onSelectionChange={(d) => setSelected(d.selectedValue)}
      />
      <Text fontSize="sm" color="fg.muted">
        Selected: {selected.join(", ") || "none"}
      </Text>
    </Stack>
  )
}
```

### No chevron, no guides, multiple selection

Turn off the chevron with `showChevron={false}` and the guide lines with `indentGuides={false}`. `selectionMode` comes straight from Tree View.

```tsx
function FileTreeMinimal() {
  return <FileTree items={files} showChevron={false} indentGuides={false} selectionMode="multiple" defaultExpandedValue={["src"]} maxW="xs" />
}
```

## Props

### FileTree

| Prop | Type | Default | Description |
| --- | --- | --- | --- |
| `items` * | `FileTreeNode[]` |  | Top-level nodes. |
| `label` | `ReactNode` |  | Heading shown above the tree. |
| `folderIcon` | `ReactNode \| ((node: FileTreeNode) => ReactNode)` | `a folder icon` | Icon of a closed folder. |
| `folderOpenIcon` | `ReactNode \| ((node: FileTreeNode) => ReactNode)` | `an open folder icon` | Icon of an open folder. |
| `fileIcon` | `ReactNode \| ((node: FileTreeNode) => ReactNode)` | `a document icon` | Icon of a file; pass a function to choose by name or extension. |
| `showChevron` | `boolean` | `true` | Show the chevron before folders. |
| `indentGuides` | `boolean` | `true` | Vertical guide lines for nested levels. |

`*` = required. Style props and HTML attributes are accepted too.

## Accessibility

- Same behavior as Tree View: WAI-ARIA tree pattern, arrow keys to move and expand or collapse, Home and End, Enter or Space to select.
- Default icons are decorative (`aria-hidden`). When you pass your own icons, keep them decorative too; the node name carries the meaning.

## Notes

- `items` should be a stable reference (module constant or `useMemo`). A new array on every render rebuilds the tree.
- Node `id` must be unique across the whole tree, not just among siblings. Paths such as `src/components/button.tsx` work well.
- Default icons are inline SVG, so the component adds no icon dependency. Any React element works as a custom icon.

## See also

- Tree View: https://ui.indoku.org/docs/tree-view
- Accordion: https://ui.indoku.org/docs/accordion
