Vize

Tree

Accessible, unstyled, data-driven tree view with typed nodes, lazy loading, and virtualization.

Package @vizejs/ui/tree
Maturity stable
Own the source vize lib pull tree
Requires collection, context, controllable-state, id, sortable, typeahead, virtualizer
Aliases tree view, treeview, file tree, hierarchy, outline, nested list
Covers WAI-ARIA tree view pattern, React Aria Tree, Ark UI Tree View, Reka UI Tree, MUI Tree View

Usage

import { Tree, TreeRoot, TreeItem, TreeItemCheckbox, TreeItemToggle } from "@vizejs/ui/tree";

Or copy the source into your project with vize lib pull tree (see Source Distribution).

API

Tree<T, K extends TreeKey>

Source: src/families/data/tree/tree-root.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned tree base id. null and undefined select a deterministic fallback.
items (required) readonly T[] required Root nodes in display order.
getKey (required) (node: T) => K required Resolve a node's stable key. Keys must be unique across the whole tree.
getChildren (node: T) => readonly T[] | null | undefined undefined Resolve a node's children. null and undefined mean "no children yet", which lets loadChildren fetch them lazily; an empty array marks a leaf.
hasChildren (node: T) => boolean () => true Whether a node without resolved children may lazily load some. Only consulted with loadChildren.
loadChildren (node: T, context: TreeLoadContext) => Promise<readonly T[]> undefined Asynchronously load a node's children the first time it expands.
getTextValue (node: T) => string undefined Text used by typeahead. undefined reads the rendered row text, so virtualized trees should provide it to reach rows outside the window.
isDisabled (node: T) => boolean undefined Disable individual nodes. Disabled rows stay focusable but cannot expand, select, or check.
expanded readonly K[] undefined Controlled expanded keys (v-model:expanded). undefined selects uncontrolled behavior.
defaultExpanded readonly K[] [] Initially expanded keys for uncontrolled use.
selected readonly K[] undefined Controlled selected keys (v-model:selected). Single mode keeps at most one key.
defaultSelected readonly K[] [] Initially selected keys for uncontrolled use.
checked readonly K[] undefined Controlled checked keys (v-model:checked), normalized by checkPropagation.
defaultChecked readonly K[] [] Initially checked keys for uncontrolled use. A cascading parent key checks its subtree.
selectionMode TreeSelectionMode "single" Selection model: none, one key, or a set of keys.
selectionFollowsFocus boolean false Whether arrow-key focus also selects in single selection mode.
expandOnClick boolean false Whether clicking an expandable row also toggles it.
checkable boolean false Expose aria-checked on rows and make Space toggle checkboxes instead of selection.
checkPropagation TreeCheckPropagation "cascade" How checking a node affects its descendants and ancestors.
disabled boolean false Disable the whole tree and remove it from sequential focus order.
dir TreeDirection undefined Reading direction used to map ArrowRight and ArrowLeft to expand and collapse. undefined inherits DirectionProvider/LocaleProvider, then "ltr".
typeahead boolean true Move focus to the next row whose text starts with typed characters.
typeaheadTimeout number 500 Idle time in milliseconds before typeahead starts a new query.
virtualizer TreeVirtualizer undefined Windowing adapter from useTreeVirtualizer. The tree element becomes the scroll viewport.
reorder TreeReorderController<K> undefined Drag and Alt+Arrow reorder adapter from useTreeReorder.
ariaLabel string undefined Accessible name when no visible label or aria-labelledby supplies one.
ariaLabelledby string undefined Space-separated ids that label the tree.
ariaDescribedby string undefined Space-separated ids that describe the tree.

Events

Event Payload Description
update:expanded [keys: readonly K[]] Fired with the next expanded keys whenever expansion changes.
update:selected [keys: readonly K[]] Fired with the next selected keys whenever selection changes.
update:checked [keys: readonly K[]] Fired with the next normalized checked keys whenever a checkbox changes.
action [key: K, node: T, nativeEvent: Event] Fired when Enter or a double click activates a row, for example to open a file.
load [key: K, children: readonly T[]] Fired after loadChildren resolves for a node.
loadError [key: K, error: unknown] Fired when loadChildren rejects for a node.

Slots

Slot Slot props Description
default TreeSlotState<T, K> TreeItem rows. Receives rendered rows, every visible row, and selection state.

Exposed

Member Type Description
activeKey —
checked —
collapse —
collapseAll —
element Readonly<ShallowRef<HTMLElement | null>> Template ref to the rendered root element.
expand —
expandAll —
expanded —
focus —
focusKey —
getCheckedState —
id —
reload —
selected —
setSelected —
toggle —
toggleChecked —

TreeRoot<T, K extends TreeKey>

Source: src/families/data/tree/tree-root.vue

Props

Prop Type Default Description
id string | null undefined Consumer-owned tree base id. null and undefined select a deterministic fallback.
items (required) readonly T[] required Root nodes in display order.
getKey (required) (node: T) => K required Resolve a node's stable key. Keys must be unique across the whole tree.
getChildren (node: T) => readonly T[] | null | undefined undefined Resolve a node's children. null and undefined mean "no children yet", which lets loadChildren fetch them lazily; an empty array marks a leaf.
hasChildren (node: T) => boolean () => true Whether a node without resolved children may lazily load some. Only consulted with loadChildren.
loadChildren (node: T, context: TreeLoadContext) => Promise<readonly T[]> undefined Asynchronously load a node's children the first time it expands.
getTextValue (node: T) => string undefined Text used by typeahead. undefined reads the rendered row text, so virtualized trees should provide it to reach rows outside the window.
isDisabled (node: T) => boolean undefined Disable individual nodes. Disabled rows stay focusable but cannot expand, select, or check.
expanded readonly K[] undefined Controlled expanded keys (v-model:expanded). undefined selects uncontrolled behavior.
defaultExpanded readonly K[] [] Initially expanded keys for uncontrolled use.
selected readonly K[] undefined Controlled selected keys (v-model:selected). Single mode keeps at most one key.
defaultSelected readonly K[] [] Initially selected keys for uncontrolled use.
checked readonly K[] undefined Controlled checked keys (v-model:checked), normalized by checkPropagation.
defaultChecked readonly K[] [] Initially checked keys for uncontrolled use. A cascading parent key checks its subtree.
selectionMode TreeSelectionMode "single" Selection model: none, one key, or a set of keys.
selectionFollowsFocus boolean false Whether arrow-key focus also selects in single selection mode.
expandOnClick boolean false Whether clicking an expandable row also toggles it.
checkable boolean false Expose aria-checked on rows and make Space toggle checkboxes instead of selection.
checkPropagation TreeCheckPropagation "cascade" How checking a node affects its descendants and ancestors.
disabled boolean false Disable the whole tree and remove it from sequential focus order.
dir TreeDirection undefined Reading direction used to map ArrowRight and ArrowLeft to expand and collapse. undefined inherits DirectionProvider/LocaleProvider, then "ltr".
typeahead boolean true Move focus to the next row whose text starts with typed characters.
typeaheadTimeout number 500 Idle time in milliseconds before typeahead starts a new query.
virtualizer TreeVirtualizer undefined Windowing adapter from useTreeVirtualizer. The tree element becomes the scroll viewport.
reorder TreeReorderController<K> undefined Drag and Alt+Arrow reorder adapter from useTreeReorder.
ariaLabel string undefined Accessible name when no visible label or aria-labelledby supplies one.
ariaLabelledby string undefined Space-separated ids that label the tree.
ariaDescribedby string undefined Space-separated ids that describe the tree.

Events

Event Payload Description
update:expanded [keys: readonly K[]] Fired with the next expanded keys whenever expansion changes.
update:selected [keys: readonly K[]] Fired with the next selected keys whenever selection changes.
update:checked [keys: readonly K[]] Fired with the next normalized checked keys whenever a checkbox changes.
action [key: K, node: T, nativeEvent: Event] Fired when Enter or a double click activates a row, for example to open a file.
load [key: K, children: readonly T[]] Fired after loadChildren resolves for a node.
loadError [key: K, error: unknown] Fired when loadChildren rejects for a node.

Slots

Slot Slot props Description
default TreeSlotState<T, K> TreeItem rows. Receives rendered rows, every visible row, and selection state.

Exposed

Member Type Description
element HTMLDivElement | null Rendered tree element.
id string Root-owned base id.
expanded readonly K[] Currently expanded keys.
selected readonly K[] Currently selected keys.
checked readonly K[] Normalized checked keys.
activeKey K | null Key owning roving focus.
focus (options?: FocusOptions) => void Move focus to the active, selected, or first row.
focusKey (key: K, options?: FocusOptions) => boolean Make a visible key active and focus it, scrolling a virtual window when needed.
expand (key: K) => Promise<boolean> Expand one node, loading lazy children first when needed.
collapse (key: K) => boolean Collapse one node.
toggle (key: K) => Promise<boolean> Toggle one node's expansion.
expandAll () => boolean Expand every node whose children are already resolved.
collapseAll () => boolean Collapse every node.
setSelected (keys: readonly K[]) => boolean Replace the selected keys. Single mode keeps at most the first key.
toggleChecked (key: K) => boolean Toggle one node's checkbox with the configured propagation.
getCheckedState (key: K) => TreeCheckedState Resolve the tri-state checkbox value of any known node.
reload (key: K) => Promise<boolean> Reload lazy children for one node.

TreeItem<T, K extends TreeKey>

Source: src/families/data/tree/tree-item.vue

Props

Prop Type Default Description
item (required) TreeFlatNode<T, K> required Visible row from the TreeRoot slot items.
ariaLabel string undefined Accessible name when the row text does not supply one.
ariaLabelledby string undefined Space-separated ids that label the row.
ariaDescribedby string undefined Space-separated ids that describe the row.

Slots

Slot Slot props Description
default TreeItemSlotState<T, K> Row content. Receives the typed node plus expansion, selection, and checkbox state.

Exposed

Member Type Description
node T Consumer-owned node data.
key K Stable node key.
level number One-based depth.
expandable boolean Whether the node has, or may lazily load, children.
expanded boolean Whether the node is expanded.
selected boolean Whether the node is selected.
checked TreeCheckedState Tri-state checkbox value.
disabled boolean Whether the node or the tree is disabled.
active boolean Whether the node owns roving focus.
loadState TreeLoadState Lazy children lifecycle for this node.
state TreeItemState Stable expansion state token.
element HTMLDivElement | null Rendered treeitem element.
id string Deterministic treeitem id.
focus (options?: FocusOptions) => void Make this row active and focus it.

TreeItemCheckbox

Source: src/families/data/tree/tree-item-checkbox.vue

Slots

Slot Slot props Description
default { /** Tri-state checkbox value of the owning row. */ readonly checked: TreeCheckedState; /** Whether the owning row or the tree is disabled. */ readonly disabled: boolean; } Checkbox indicator content. Receives the tri-state value of the owning row.

TreeItemToggle

Source: src/families/data/tree/tree-item-toggle.vue

Slots

Slot Slot props Description
default { /** Whether the owning row has, or may lazily load, children. */ readonly expandable: boolean; /** Whether the owning row is expanded. */ readonly expanded: boolean; /** Lazy children lifecycle for the owning row. */ readonly loadState: TreeLoadState; /** Stable expansion state token. */ readonly state: TreeItemState; } Expand indicator content, for example a chevron. Receives expansion and load state.

Behavior

Normative state x input -> outcome table for tree-root.vue, tree-item.vue, tree-item-toggle.vue, and tree-item-checkbox.vue (@vizejs/ui/tree), plus the useTreeVirtualizer and useTreeReorder adapters. Every row is proven by the named test in tree.test.ts, tree-adapters.test.ts, tree-model.test.ts, or tree-ssr.test.ts; compile-only guarantees live in tree.types.test-d.ts.

The tree follows the WAI-ARIA APG tree view pattern. It renders flat: every visible node is one role="treeitem" row carrying aria-level, aria-setsize, and aria-posinset, so DOM order equals visual order and rows can be virtualized without nested role="group" containers. Nodes are consumer-owned data of any type T; getKey and getChildren are typed against T, and the key type K is inferred from getKey and flows into every v-model, emit, slot, and exposed method.

ID State Input Outcome Evidence
T1 default render tree with flat treeitem rows, levels, set positions, aria-expanded on parents only, roving tabindex renders APG tree semantics with flat levels, set positions, and roving tabindex
T2 focus on a row ArrowDown/Up/Right/Left, Home, End APG movement: Right expands then enters, Left collapses then moves to the parent, Home/End jump arrow keys, Home, and End follow the APG tree keyboard model
T3 focus inside a branch collapse an ancestor focus and the active key move to the collapsed ancestor instead of being lost collapsing an ancestor moves focus from a hidden descendant to the ancestor
T4 dir="rtl" ArrowLeft / ArrowRight horizontal expand and collapse keys swap RTL maps ArrowLeft to expand and ArrowRight to collapse
T5 single selection click, Enter, focus movement click and Enter select one key; Enter and double click emit action; selectionFollowsFocus opt-in single selection follows click and Enter, and emits action for activation
T6 multiple selection click, Shift+click, Space, Shift+Arrow, Ctrl/Cmd+A toggles, anchor ranges, focus extension, and select-all/clear-all with aria-multiselectable multiple selection toggles, extends ranges, and selects all
T7 selectionMode="none" click, Enter no aria-selected, no selection emits; action still fires selection mode none omits aria-selected and ignores selection input
T8 checkable cascade Space, checkbox click checks cascade to descendants; ancestors derive checked/mixed/unchecked in aria-checked checkboxes cascade to descendants and derive tri-state ancestors
T9 checkPropagation="independent" Space only the requested node toggles independent checkboxes toggle only the requested node
T10 lazy branch expand loadChildren runs once, row is aria-busy while loading, children inherit a checked parent, cached lazy children load on expand with busy state, load events, and inherited checks
T11 lazy branch load rejects, then reload() data-load-state="error" and loadError; reload retries and renders children failed lazy loads expose an error state and can reload
T12 focus on a row printable characters typeahead focuses the next row whose text matches, using the shared typeahead buffer typeahead moves focus to the next row whose text matches
T13 any *, expandAll(), collapseAll() * expands the focused row's siblings; expand-all covers every resolved branch asterisk expands siblings and expandAll/collapseAll cover every resolved branch
T14 disabled row / disabled tree click, keys, Tab disabled rows stay focusable but never expand, select, or check; a disabled tree leaves the tab order disabled rows stay focusable but cannot expand, select, or check
T15 controlled expanded/selected user input update events fire while rendered state waits for the parent controlled expansion and selection wait for the parent to accept requests
T16 toggle part / expandOnClick click the toggle expands without selecting and keeps focus on the row the toggle expands on click without selecting, and expandOnClick toggles rows
T17 exposed instance imperative calls typed focus, focusKey, expand, toggle, setSelected, toggleChecked, getCheckedState exposes typed state and imperative focus, selection, and checkbox controls
T18 missing provider setup compound parts fail closed with the shared context diagnostic compound parts require a matching root provider
T19 useTreeVirtualizer render only the window renders, every row still reports its full-tree set metadata a virtualizer renders only the window and keeps set metadata for every row
T20 useTreeVirtualizer End, typeahead focus scrolls unmounted rows into the window first; typeahead reaches them through getTextValue keyboard focus scrolls rows outside the window into view before focusing them
T21 active row scrolled out Tab into the tree the tree element becomes the tab stop and forwards focus to the active row the tree element takes sequential focus while the active row is outside the window
T22 useTreeReorder Alt+ArrowUp/Down/Right/Left onMove receives sibling, nesting, and outdent requests; data is never mutated by the tree Alt+Arrow keys request sibling, nesting, and outdent moves
T23 useTreeReorder with canMove move request vetoed and self-targeted moves are dropped; rows register with the shared pointer sortable engine canMove vetoes moves and rows register with the pointer sort engine
T24 model helpers index, flatten, derive, toggle pre-order indexing, duplicate-key diagnostic, tri-state derivation, disabled-aware cascade tree-model.test.ts
T25 SSR and hydration isolated render/mount byte-identical markup, virtual window from initialRect, hydration without warnings tree-ssr.test.ts

Notes

  • Lazy branches that start expanded load after mount, so server and client render the same idle markup before any request runs.
  • The toggle and checkbox parts are aria-hidden pointer affordances: keyboard users expand with arrows and check with Space on the row itself, which keeps interactive controls from nesting inside a treeitem.
  • Keys are compared with Object.is; 1 and "1" are distinct keys but must not both appear in one tree when reordering, because the drag engine keys rows by String(key).
  • No styles ship with the primitive. Rows expose data-state, data-level, data-selected, data-checked, data-load-state, data-dragging, and data-drop-position; indentation and virtual offsets are consumer-owned.