Command Palette

Search for a command to run...

Tree

A data-driven hierarchical tree view of folders and leaves with selection, expand/collapse, per-node icons, an actions slot, and a custom renderItem. Self-managed expand state, animated open/close, ARIA roving-tabindex keyboard navigation, and native drag-and-drop to reorder or re-parent nodes via onDocumentDrag. Controlled or uncontrolled selection reported through onSelectChange.

A data-driven hierarchical tree view. Pass a data array of TreeDataItem nodes — any node with children is a folder that expands and collapses, anything else is a leaf. The tree manages its own expand state (auto-expanding the ancestors of the selected node, or everything when expandAll is set) and its selection — controlled with selectedId or uncontrolled from defaultSelectedId, reporting changes through onSelectChange. Each node can carry its own icons, an actions slot, and a disabled flag, or you can take over the whole row with renderItem. Mark nodes draggable / droppable to reorder or re-parent them with native drag-and-drop through onDocumentDrag.

Installation

One-time setup: add the @ikui registry to your components.json.

components.json
{
  "registries": {
    "@ikui": "https://ik-ui.pages.dev/r/{name}.json"
  }
}

Then install the component:

pnpm dlx shadcn@latest add @ikui/tree

Usage

import { Tree, type TreeDataItem } from "@/components/tree";
const data: TreeDataItem[] = [
  {
    id: "app",
    name: "app",
    children: [
      { id: "page", name: "page.tsx" },
      { id: "layout", name: "layout.tsx" },
    ],
  },
  { id: "readme", name: "README.md" },
];

<Tree
  data={data}
  defaultSelectedId="page"
  onSelectChange={(item) => console.log(item?.name)}
/>;

Keyboard

The tree follows the ARIA tree pattern with a roving tabindex, so it takes a single stop in the tab order. Once focused, navigate entirely with the keyboard:

  • / — move to the previous / next visible node (disabled nodes are skipped).
  • — expand a collapsed folder, or move to its first child when already expanded.
  • — collapse an expanded folder, or move to its parent.
  • Home / End — jump to the first / last visible node.
  • Enter / Space — select the node (and toggle it when it is a folder).

Examples

Per-node icons

Set icon on a node, plus openIcon (shown while a folder is expanded) and selectedIcon (shown while selected). defaultNodeIcon / defaultLeafIcon fill in any node that doesn't specify its own.

Tracking selection

onSelectChange fires with the clicked TreeDataItem (folders included). The selected row stays highlighted.

Row actions

Give a node an actions node to render trailing controls — revealed on hover or when the row is selected. Clicks inside actions don't select or toggle the row.

Drag and drop

Set draggable on the nodes you can pick up and droppable (default true) on the ones that can receive a drop. onDocumentDrag(source, target) fires on drop — the tree only tracks the gesture and highlights the target; you own the data, so reorder or re-parent it in the handler. Here, dropping a file onto a folder moves it in.

Props

PropTypeDefault
data
TreeDataItem[] | TreeDataItem
-
selectedId
string
-
defaultSelectedId
string
-
initialSelectedItemId
string
-
onSelectChange
(item: TreeDataItem) => void
-
expandAll
boolean
false
chevronPosition
"left" | "right"
"left"
defaultNodeIcon
React.ComponentType<{ className?: string }>
-
defaultLeafIcon
React.ComponentType<{ className?: string }>
-
renderItem
(params: TreeRenderItemParams) => React.ReactNode
-
onDocumentDrag
(source: TreeDataItem, target: TreeDataItem) => void
-
className
string
-

TreeDataItem

PropTypeDefault
id
string
-
name
string
-
children
TreeDataItem[]
-
icon
React.ComponentType<{ className?: string }>
-
openIcon
React.ComponentType<{ className?: string }>
-
selectedIcon
React.ComponentType<{ className?: string }>
-
actions
React.ReactNode
-
onClick
() => void
-
disabled
boolean
-
draggable
boolean
-
droppable
boolean
true
className
string
-

TreeRenderItemParams

PropTypeDefault
item
TreeDataItem
-
level
number
-
isLeaf
boolean
-
isSelected
boolean
-
isOpen
boolean
-
hasChildren
boolean
-