Skip to main content
0.77.0
View Zag.js on Github
Join the Discord server

Tree View

The TreeView component provides a hierarchical view of data, similar to a file system explorer. It allows users to expand and collapse branches, select individual or multiple nodes, and traverse the hierarchy using keyboard navigation.

My Documents

panda.config.ts
package.json
renovate.json
README.md
Properties

Features

  • Display hierarchical data in a tree structure.
  • Expand or collapse nodes
  • Support for keyboard navigation
  • Select single or multiple nodes (depending on the selection mode)
  • Perform actions on the nodes, such as deleting them or performing some other operation.

Installation

To use the tree view machine in your project, run the following command in your command line:

npm install @zag-js/tree-view @zag-js/react # or yarn add @zag-js/tree-view @zag-js/react

This command will install the framework agnostic tree view logic and the reactive utilities for your framework of choice.

Anatomy

To set up the tree view correctly, you'll need to understand its anatomy.

Usage

First, import the tree view package into your project

import * as tree from "@zag-js/tree-view"

The tree view package exports two key functions:

  • machine — The state machine logic for the tree view widget.
  • connect — The function that translates the machine's state to JSX attributes and event handlers.

Next, import the required hooks and functions for your framework and use the tree view machine in your project 🔥

Create the tree collection

Use the collection function to create a tree collection. This create a tree factory that the component uses for traversal.

import * as tree from "@zag-js/tree-view" interface Node { id: string name: string children?: Node[] } const collection = tree.collection<Node>({ nodeToValue: (node) => node.id, nodeToString: (node) => node.name, rootNode: { id: "ROOT", name: "", children: [ { id: "node_modules", name: "node_modules", children: [ { id: "node_modules/zag-js", name: "zag-js" }, { id: "node_modules/pandacss", name: "panda" }, { id: "node_modules/@types", name: "@types", children: [ { id: "node_modules/@types/react", name: "react" }, { id: "node_modules/@types/react-dom", name: "react-dom" }, ], }, ], }, ], }, })

Create the tree view

Pass the tree collection to the machine to create the tree view.

import { normalizeProps, useMachine } from "@zag-js/react" import * as tree from "@zag-js/tree-view" import { FileIcon, FolderIcon, ChevronRightIcon } from "lucide-react" import { useId } from "react" // 1. Create the tree collection interface Node { id: string name: string children?: Node[] } const collection = tree.collection<Node>({ // ... }) // 2. Create the recursive tree node interface TreeNodeProps { node: Node indexPath: number[] api: tree.Api } const TreeNode = (props: TreeNodeProps): JSX.Element => { const { node, indexPath, api } = props const nodeProps = { indexPath, node } const nodeState = api.getNodeState(nodeProps) if (nodeState.isBranch) { return ( <div {...api.getBranchProps(nodeProps)}> <div {...api.getBranchControlProps(nodeProps)}> <FolderIcon /> <span {...api.getBranchTextProps(nodeProps)}>{node.name}</span> <span {...api.getBranchIndicatorProps(nodeProps)}> <ChevronRightIcon /> </span> </div> <div {...api.getBranchContentProps(nodeProps)}> <div {...api.getBranchIndentGuideProps(nodeProps)} /> {node.children?.map((childNode, index) => ( <TreeNode key={childNode.id} node={childNode} indexPath={[...indexPath, index]} api={api} /> ))} </div> </div> ) } return ( <div {...api.getItemProps(nodeProps)}> <FileIcon /> {node.name} </div> ) } // 3. Create the tree view export function TreeView() { const [state, send] = useMachine(tree.machine({ id: useId(), collection })) const api = tree.connect(state, send, normalizeProps) return ( <div {...api.getRootProps()}> <h3 {...api.getLabelProps()}>My Documents</h3> <div {...api.getTreeProps()}> {collection.rootNode.children?.map((node, index) => ( <TreeNode key={node.id} node={node} indexPath={[index]} api={api} /> ))} </div> </div> ) }

Expanding and Collapsing Nodes

By default, the tree view will expand or collapse when clicking the branch control. To control the expanded state of the tree view, use the api.expand and api.collapse methods.

api.expand(["node_modules/pandacss"]) // expand a single node api.expand() // expand all nodes api.collapse(["node_modules/pandacss"]) // collapse a single node api.collapse() // collapse all nodes

Multiple selection

The tree view supports multiple selection. To enable this, set the selectionMode to multiple.

const [state, send] = useMachine( tree.machine({ selectionMode: "multiple", }), )

Setting the default expanded nodes

To set the default expanded nodes, use the expandedValue context property.

const [state, send] = useMachine( tree.machine({ expandedValue: ["node_modules/pandacss"], }), )

Setting the default selected nodes

To set the default selected nodes, use the selectedValue context property.

const [state, send] = useMachine( tree.machine({ selectedValue: ["node_modules/pandacss"], }), )

Indentation Guide

When rendering a branch node in the tree view, you can render the indentGuide element by using the api.getBranchIndentGuideProps() function.

<div {...api.getBranchProps(nodeProps)}> <div {...api.getBranchControlProps(nodeProps)}> <FolderIcon /> {node.name} <span {...api.getBranchIndicatorProps(nodeProps)}> <ChevronRightIcon /> </span> </div> <div {...api.getBranchContentProps(nodeProps)}> <div {...api.getBranchIndentGuideProps(nodeProps)} /> {node.children.map((childNode, index) => ( <TreeNode key={childNode.id} node={childNode} indexPath={[...indexPath, index]} api={api} /> ))} </div> </div>

Listening for selection

When a node is selected, the onSelectionChange callback is invoked with the selected nodes.

const [state, send] = useMachine( tree.machine({ onSelectionChange(details) { console.log("selected nodes:", details) }, }), )

Listening for expanding and collapsing

When a node is expanded or collapsed, the onExpandedChange callback is invoked with the expanded nodes.

const [state, send] = useMachine( tree.machine({ onExpandedChange(details) { console.log("expanded nodes:", details) }, }), )

Methods and Properties

Machine Context

The tree view machine exposes the following context properties:

  • collectionTreeCollection<T>The tree collection data
  • idsPartial<{ root: string; tree: string; label: string; node(value: string): string; }>The ids of the tree elements. Useful for composition.
  • expandedValuestring[]The id of the expanded nodes
  • selectedValuestring[]The id of the selected nodes
  • focusedValuestringThe id of the focused node
  • selectionMode"single" | "multiple"Whether the tree supports multiple selection - "single": only one node can be selected - "multiple": multiple nodes can be selected
  • onExpandedChange(details: ExpandedChangeDetails) => voidCalled when the tree is opened or closed
  • onSelectionChange(details: SelectionChangeDetails) => voidCalled when the selection changes
  • onFocusChange(details: FocusChangeDetails) => voidCalled when the focused node changes
  • expandOnClickbooleanWhether clicking on a branch should open it or not
  • typeaheadbooleanWhether the tree supports typeahead search
  • dir"ltr" | "rtl"The document's text/writing direction.
  • idstringThe unique identifier of the machine.
  • getRootNode() => ShadowRoot | Node | DocumentA root node to correctly resolve document in custom environments. E.x.: Iframes, Electron.

Machine API

The tree view api exposes the following methods:

  • collectionTreeCollection<V>The tree collection data
  • expandedValuestring[]The id of the expanded nodes
  • setExpandedValue(value: string[]) => voidFunction to set the expanded value
  • selectedValuestring[]The id of the selected nodes
  • setSelectedValue(value: string[]) => voidFunction to set the selected value
  • getVisibleNodes() => V[]Function to get the visible nodes
  • expand(value?: string[]) => voidFunction to expand nodes. If no value is provided, all nodes will be expanded
  • collapse(value?: string[]) => voidFunction to collapse nodes If no value is provided, all nodes will be collapsed
  • select(value?: string[]) => voidFunction to select nodes If no value is provided, all nodes will be selected
  • deselect(value?: string[]) => voidFunction to deselect nodes If no value is provided, all nodes will be deselected
  • focus(value: string) => voidFunction to focus an item node
  • selectParent(value: string) => voidFunction to select the parent node of the focused node
  • expandParent(value: string) => voidFunction to expand the parent node of the focused node

Data Attributes

Item
data-scope
tree-view
data-part
item
data-path
The path of the item
data-value
The value of the item
data-focus
Present when focused
data-selected
Present when selected
data-disabled
Present when disabled
data-depth
The depth of the item
ItemText
data-scope
tree-view
data-part
item-text
data-disabled
Present when disabled
data-selected
Present when selected
data-focus
Present when focused
ItemIndicator
data-scope
tree-view
data-part
item-indicator
data-disabled
Present when disabled
data-selected
Present when selected
data-focus
Present when focused
Branch
data-scope
tree-view
data-part
branch
data-depth
The depth of the item
data-value
The value of the item
data-path
The path of the item
data-selected
Present when selected
data-state
"open" | "closed"
data-disabled
Present when disabled
BranchIndicator
data-scope
tree-view
data-part
branch-indicator
data-state
"open" | "closed"
data-disabled
Present when disabled
data-selected
Present when selected
data-focus
Present when focused
BranchTrigger
data-scope
tree-view
data-part
branch-trigger
data-disabled
Present when disabled
data-state
"open" | "closed"
data-value
The value of the item
BranchControl
data-scope
tree-view
data-part
branch-control
data-path
The path of the item
data-state
"open" | "closed"
data-disabled
Present when disabled
data-selected
Present when selected
data-focus
Present when focused
data-value
The value of the item
data-depth
The depth of the item
BranchText
data-scope
tree-view
data-part
branch-text
data-disabled
Present when disabled
data-state
"open" | "closed"
BranchContent
data-scope
tree-view
data-part
branch-content
data-state
"open" | "closed"
BranchIndentGuide
data-scope
tree-view
data-part
branch-indent-guide
data-depth
The depth of the item

Accessibility

Adheres to the Tree View WAI-ARIA design pattern.

Keyboard Interactions

  • Tab
    Moves focus to the tree view, placing the first tree view item in focus.
  • EnterSpace
    Selects the item or branch node
  • ArrowDown
    Moves focus to the next node
  • ArrowUp
    Moves focus to the previous node
  • ArrowRight
    When focus is on a closed branch node, opens the branch.
    When focus is on an open branch node, moves focus to the first item node.
  • ArrowLeft
    When focus is on an open branch node, closes the node.
    When focus is on an item or branch node, moves focus to its parent branch node.
  • Home
    Moves focus to first node without opening or closing a node.
  • End
    Moves focus to the last node that can be focused without expanding any nodes that are closed.
  • a-zA-Z
    Focus moves to the next node with a name that starts with the typed character. The search logic ignores nodes that are descendants of closed branch.
  • *
    Expands all sibling nodes that are at the same depth as the focused node.
  • Shift + ArrowDown
    Moves focus to and toggles the selection state of the next node.
  • Shift + ArrowUp
    Moves focus to and toggles the selection state of the previous node.
  • Ctrl + A
    Selects all nodes in the tree. If all nodes are selected, unselects all nodes.

Edit this page on GitHub

Proudly made in🇳🇬by Segun Adebayo

Copyright © 2024
On this page