Skip to content
TopClass UI

Tree view

Nodes in levels that open and close. Open branches flatten into one windowed list, so 10,000 nodes stay fast.

client - size not measured yet

Tree view

client - size not measured yet

Nodes in levels that open and close. Open branches flatten into one windowed list, so 10,000 nodes stay fast.

Install

Try it

visibleRows
loadMoreThreshold
status

The code

tree-view.tsx
import { fileText } from 'topclassui/icons/file-text'
import { folder } from 'topclassui/icons/folder'
import { Svg } from 'topclassui/svg'
import { TreeView, type TreeViewDesignProps } from 'topclassui/tree-view'

<TreeView aria-label="Files" items={ITEMS} defaultExpanded={['src', 'app']} />

items is plain data and a node's icon is an element, so a Server Component passes the tree as it is; there is no getKey.

Every state

4 states
app
layout.tsx
page.tsx
docs
guide.md
faq.md
next.config.ts
next.config.ts
Loading more
Loading
Nothing here yet

Import from the package

topclassui/tree-view
import { TreeView } from 'topclassui/tree-view'

Install once:npm i topclassui@beta zod

Examples

  • A node is { id, label, icon?, disabled?, children? }. The id is the key; a node with children, even [], is a branch.
  • To load a branch's children, listen to onExpandedChange and pass them in through items.
  • loadMore asks for the next page of root nodes, once per page, while hasMore. The tree never fetches.
  • visibleRows sets how many rows show before the tree scrolls; left out, the --tc-list-rows knob applies. virtualize windows above 200 visible rows.
  • status marks the tree busy while loading or loading-more, and says there is nothing when empty.
  • className, style, ref and every other prop reach the element with role="tree", not the box around it.
basic
src
app
layout.tsx
page.tsx
package.json
README.md
selected
Project
app
layout.tsx
page.tsx
next.config.ts
load-more
Repository

Pages asked for: 0, loaded: 1

status
Loading
Nothing here yet
ten-thousand
Folder 1
File 1.ts
File 2.ts
File 3.ts
File 4.ts
File 5.ts
File 6.ts
File 7.ts
File 8.ts
File 9.ts
File 10.ts
File 11.ts
File 12.ts
File 13.ts
File 14.ts
File 15.ts
File 16.ts
File 17.ts
File 18.ts
long-content
SupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilistic
تقرير report 2026 النهائي final version with a long name that goes on
Level 1
Level 2
Level 3
Level 4
Level 5
Level 6
Level 7
Level 8
Level 9
Level 10, SupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilisticSupercalifragilistic
Sixty items
Item 1
Item 2
Item 3
Item 4
Item 5
Item 6
Item 7
Item 8
Item 9
Item 10
Item 11
Item 12
Item 13
Item 14
Item 15
Item 16
Item 17
Item 18
Item 19
Item 20
Item 21
Item 22
Item 23
Item 24
Item 25
Item 26
Item 27
Item 28
Item 29
Item 30
Item 31
Item 32
Item 33
Item 34
Item 35
Item 36
Item 37
Item 38
Item 39
Item 40
Item 41
Item 42
Item 43
Item 44
Item 45
Item 46
Item 47
Item 48
Item 49
Item 50
Item 51
Item 52
Item 53
Item 54
Item 55
Item 56
Item 57
Item 58
Item 59
Item 60
In Arabic, right to left
المستندات
التقرير السنوي.pdf
الميزانية.xlsx
ملاحظات.txt

Props

The props of TreeView
PropTypeDefaultLimitsDescription
items*readonly TreeNode[]--The nodes, as plain data. Keep the array stable between renders.
aria-labelstring--The name read out when no visible text names the group.
aria-labelledbystring--The id of the visible text that names the group.
classNamestring--On the element with role tree, merged last.
defaultExpandedreadonly string[]--The ids open when the tree mounts.
defaultValuestring | null--The id selected when the tree mounts.
emptyFallbackReactNode--What status empty says, in place of labels.state.empty.
expandedreadonly string[]--The ids of the open branches. Use with onExpandedChange when controlled.
hasMoreboolean--More root nodes exist, so loadMore is called when the tree nears its end.
loadingFallbackReactNode--What status loading says, in place of labels.common.loading.
loadMore(() => void | Promise<void>)--Called with no argument for the next page of root nodes, once per page; never for a branch. Client-only, or a Server Action.
loadMoreThresholdinteger, min 0, max 20010min 0, max 200How many rows from the end loadMore is called, clamped.
onExpandedChange((ids: readonly string[]) => void)--Called with every open id after a branch opens or closes: load a branch's children here. Client-only.
onValueChange((id: string) => void)--Called with the id of the node Enter, Space or a press selects. Client-only.
statusidle | loading | loading-more | empty"idle"-loading and loading-more mark the tree busy and say so under it; empty says there is nothing.
valuestring | null--The selected node's id, or null for none. Use with onValueChange when controlled.
virtualizeunknown"auto"-auto windows the tree above 200 visible rows; true always, false never.
visibleRowsinteger, min 1, max 207min 1, max 20Rows the tree shows before it scrolls, clamped. Left out, the --tc-list-rows knob applies.

Keyboard and accessibility

The keyboard of TreeView
KeysWhat it does
ArrowDownArrowUpThe next or the previous visible row; no wrap.
ArrowRightOpens a closed branch; on an open one, moves to its first child. ArrowLeft in Arabic.
ArrowLeftCloses an open branch; anything else moves to its parent. ArrowRight in Arabic.
HomeEndThe first, the last visible row.
EnterSelects the row, and opens or closes a branch; a press does the same.
SpaceSelects the row.
A–ZThe next row whose label starts with what was typed in the last 500 ms.
  • The tree is the one tab stop and keeps the focus; aria-activedescendant names the row the keys are on, so windowing never drops the focus.
  • A tree draws no name of its own: aria-label or aria-labelledby is required by the types.
  • Each row carries its level, its position among its siblings and whether it is open, so a screen reader says where it is.

Right to left

The indent is on the right and a closed caret points left. ArrowLeft opens a branch and ArrowRight closes it.

Server and client

A client component: it renders on the server and hydrates in the browser.

Files: index.tsx, client.tsx.

On the chip: client - size not measured yet.