Collection
The Collection class is designed to manage a collection of items, providing functionalities such as sorting, searching, getting next or previous items, converting items to values or strings, checking if an item is disabled, and more.
Good to know: This is used in the select and combobox components
List Collection
A list collection is a collection that is based on an array of items. It is created by passing an array of items to the constructor.
import { ListCollection } from "@zag-js/collection" const collection = new ListCollection({ items: [ { label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }, ], })
Converting value to item
You can convert a value to an item by passing the value to the find
or
findMany
method.
const item = collection.find("banana") console.log(item) // { label: "Banana", value: "banana" } const items = collection.findMany(["apple", "banana"]) console.log(items) // [{ label: "Apple", value: "apple" }, { label: "Banana", value: "banana" }]
Value Traversal
You can get the next or previous item based on a value by passing the value to
the getNextValue
or getPreviousValue
method.
const nextValue = collection.getNextValue("apple") console.log(nextValue) // banana const previousItem = collection.getPreviousValue("banana") console.log(previousItem) // apple
Likewise, you can also get the first or last item by calling the firstValue
or
lastValue
computed properties.
console.log(collection.firstValue) // apple console.log(collection.lastValue) // banana
Check for value existence
You can check if a value exists in the collection by calling the has
method.
const hasValue = collection.has("apple") console.log(hasValue) // true
Working with custom objects
If you are working with custom objects, you can pass a function to the
itemToString
and getItemValue
options to specify how to convert an item to a
string and a value, respectively.
import { ListCollection } from "@zag-js/collection" const collection = new ListCollection({ items: [ { id: 1, name: "apple" }, { id: 2, name: "banana" }, { id: 3, name: "cherry" }, ], itemToString: (item) => item.name, itemToValue: (item) => item.id, })
To disable an item, you can pass a function to the isItemDisabled
option.
import { ListCollection } from "@zag-js/collection" const collection = new ListCollection({ items: [ { id: 1, name: "apple" }, { id: 2, name: "banana" }, { id: 3, name: "cherry" }, ], isItemDisabled: (item) => item.id === 2, })
Reorder items
You can reorder items by calling the reorder
method and passing the starting
index and the ending index of the item to be moved.
const fromIndex = 1 // Banana const toIndex = 0 // Apple collection.reorder(fromIndex, toIndex) console.log(collection.items) // [{ label: "Banana", value: "banana" }, { label: "Apple", value: "apple" }]
Tree Collection
A tree collection is designed to manage hierarchical data structures like file systems, navigation menus, or organization charts. It provides powerful methods for traversing, manipulating, and querying tree structures.
import { TreeCollection } from "@zag-js/collection" const treeData = { value: "root", label: "Root", children: [ { value: "folder1", label: "Folder 1", children: [ { value: "file1", label: "File 1.txt" }, { value: "file2", label: "File 2.txt" }, ], }, { value: "folder2", label: "Folder 2", children: [ { value: "subfolder1", label: "Subfolder 1", children: [{ value: "file3", label: "File 3.txt" }], }, ], }, ], } const tree = new TreeCollection({ rootNode: treeData })
Navigation Methods
The tree collection provides various methods to navigate through the hierarchical structure.
Getting First and Last Nodes
const firstNode = tree.getFirstNode() console.log(firstNode?.value) // "folder1" const lastNode = tree.getLastNode() console.log(lastNode?.value) // "folder2"
Sequential Navigation
Navigate to the next or previous node in the tree traversal order:
const nextNode = tree.getNextNode("file1") console.log(nextNode?.value) // "file2" const previousNode = tree.getPreviousNode("file2") console.log(previousNode?.value) // "file1"
Hierarchical Relationships
Parent and Children
Get parent and descendant nodes:
// Get parent node const parentNode = tree.getParentNode("file1") console.log(parentNode?.value) // "folder1" // Get all ancestor nodes const ancestors = tree.getParentNodes("file3") console.log(ancestors.map((n) => n.value)) // ["folder2", "subfolder1"] // Get all descendant nodes const descendants = tree.getDescendantNodes("folder1") console.log(descendants.map((n) => n.value)) // ["file1", "file2"] // Get descendant values only const descendantValues = tree.getDescendantValues("folder2") console.log(descendantValues) // ["subfolder1", "file3"]
Sibling Navigation
Navigate between sibling nodes:
// Assuming we have the index path of "file1" const indexPath = tree.getIndexPath("file1") // [0, 0] const nextSibling = tree.getNextSibling(indexPath) console.log(nextSibling?.value) // "file2" const previousSibling = tree.getPreviousSibling(indexPath) console.log(previousSibling) // undefined (no previous sibling) // Get all siblings const siblings = tree.getSiblingNodes(indexPath) console.log(siblings.map((n) => n.value)) // ["file1", "file2"]
Index Path Operations
Work with index paths to identify node positions:
// Get index path for a value const indexPath = tree.getIndexPath("file3") console.log(indexPath) // [1, 0, 0] // Get value from index path const value = tree.getValue([1, 0, 0]) console.log(value) // "file3" // Get full value path (all ancestors + node) const valuePath = tree.getValuePath([1, 0, 0]) console.log(valuePath) // ["folder2", "subfolder1", "file3"] // Get node at specific index path const node = tree.at([1, 0]) console.log(node?.value) // "subfolder1"
Tree Queries
Branch and Leaf Detection
// Check if a node is a branch (has children) const folder1Node = tree.findNode("folder1") const isBranch = tree.isBranchNode(folder1Node!) console.log(isBranch) // true // Get all branch values const branchValues = tree.getBranchValues() console.log(branchValues) // ["folder1", "folder2", "subfolder1"]
Tree Traversal
Visit all nodes with custom logic:
tree.visit({ onEnter: (node, indexPath) => { console.log(`Visiting: ${node.value} at depth ${indexPath.length}`) // Skip certain branches if (node.value === "folder2") { return "skip" // Skip this branch and its children } }, })
Filtering
Create a new tree with filtered nodes:
// Keep only nodes that match criteria const filteredTree = tree.filter((node, indexPath) => { return node.value.includes("file") // Only keep files }) console.log(filteredTree.getValues()) // ["file1", "file2", "file3"]
Tree Manipulation
Adding Nodes
const newFile = { value: "newfile", label: "New File.txt" } // Insert after a specific node const indexPath = tree.getIndexPath("file1") const updatedTree = tree.insertAfter(indexPath!, [newFile]) // Insert before a specific node const updatedTree2 = tree.insertBefore(indexPath!, [newFile])
Removing Nodes
const indexPath = tree.getIndexPath("file2") const updatedTree = tree.remove([indexPath!]) console.log(updatedTree.getValues()) // file2 is removed
Moving Nodes
const fromIndexPaths = [tree.getIndexPath("file1")!] const toIndexPath = tree.getIndexPath("folder2")! const updatedTree = tree.move(fromIndexPaths, toIndexPath) // file1 is now moved under folder2
Replacing Nodes
const indexPath = tree.getIndexPath("file1")! const newNode = { value: "replacedfile", label: "Replaced File.txt" } const updatedTree = tree.replace(indexPath, newNode)
Utility Methods
Flattening
Convert the tree to a flat structure:
const flatNodes = tree.flatten() console.log( flatNodes.map((n) => ({ value: n.value, depth: n._indexPath.length })), ) // [{ value: "folder1", depth: 1 }, { value: "file1", depth: 2 }, ...]
Getting All Values
const allValues = tree.getValues() console.log(allValues) // ["folder1", "file1", "file2", "folder2", "subfolder1", "file3"]
Depth Calculation
const depth = tree.getDepth("file3") console.log(depth) // 3 (root -> folder2 -> subfolder1 -> file3)
Working with Custom Node Types
You can customize how the tree collection interprets your data:
interface CustomNode { id: string name: string items?: CustomNode[] isDisabled?: boolean } const customTree = new TreeCollection<CustomNode>({ rootNode: { id: "root", name: "Root", items: [ { id: "1", name: "Item 1", isDisabled: false }, { id: "2", name: "Item 2", isDisabled: true }, ], }, nodeToValue: (node) => node.id, nodeToString: (node) => node.name, nodeToChildren: (node) => node.items, isNodeDisabled: (node) => node.isDisabled ?? false, })
Creating Trees from File Paths
Create a tree structure from file paths:
import { filePathToTree } from "@zag-js/collection" const paths = [ "src/components/Button.tsx", "src/components/Input.tsx", "src/utils/helpers.ts", "docs/README.md", ] const fileTree = filePathToTree(paths) console.log(fileTree.getBranchValues()) // ["src", "components", "utils", "docs"]
Good to know: Tree collections are immutable - all modification methods return a new tree instance rather than modifying the original.
Edit this page on GitHub