Skip to main content
Vuetify0 is now in alpha!
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

createNested

Hierarchical tree management built on createGroup, with parent-child relationships, open/close state, and pluggable traversal strategies.

Usage

The createNested composable manages hierarchical tree structures with parent-child relationships, open/close states, and tree traversal.

ts
import { createNested } from '@vuetify/v0'

const tree = createNested({ open: 'multiple', selection: 'cascade' })

tree.onboard([
  {
    id: 'root',
    value: 'Root',
    children: [
      { id: 'child-1', value: 'Child 1' },
      { id: 'child-2', value: 'Child 2' },
    ],
  },
])

tree.open('root')
tree.select('child-1')

Architecture

createNested extends createGroup with hierarchical tree management:

Reactivity

createNested uses shallowReactive for tree state, making structural changes reactive while keeping traversal methods non-reactive for performance.

Property/MethodReactiveNotes
childrenShallowReactive Map
parentsShallowReactive Map
openedIdsShallowReactive Set
openedItemsComputed from openedIds
rootIdsShallowReactive Set — IDs of all top-level (parentless) nodes
rootsComputed, root nodes
leavesComputed, leaf nodes
ticket.isOpenRef via toRef()
ticket.isLeafRef via toRef()
ticket.depthRef via toRef()

Examples

Options

open

Controls how nodes expand/collapse:

ValueBehavior
'multiple'Multiple nodes can be open simultaneously (default)
'single'Only one node open at a time (accordion behavior)
ts
// Tree view - multiple nodes open
const tree = createNested({ open: 'multiple' })

// Accordion - single node open
const accordion = createNested({ open: 'single' })

mandatory

When true, deselecting is prevented if it would leave no items selected:

ts
const tree = createNested({ selection: 'cascade', mandatory: true })

tree.select('child-1')
tree.unselect('child-1') // no-op — would deselect the only selected item

unselectAll() with mandatory: true keeps the first selected item rather than clearing.

multiple

When false, selecting a node in cascade mode clears previous selections first (default: true):

ts
const tree = createNested({ selection: 'cascade', multiple: false })

tree.select('child-1')
tree.select('child-2') // child-1 is deselected first

disabled

When true, all tree mutations (open(), close(), select(), unselect(), toggle()) become no-ops. Individual tickets can also carry a disabled flag to skip only that node:

ts
const tree = createNested({ disabled: true })

tree.open('branch-1')   // no-op — tree is disabled
tree.select('leaf-1')   // no-op

Accepts MaybeRefOrGetter<boolean> for reactive toggling:

ts
const isLocked = shallowRef(false)
const tree = createNested({ disabled: isLocked })

selection

Controls how selection cascades through the hierarchy:

ValueBehavior
'cascade'Selecting parent selects all descendants; ancestors show mixed state (default)
'independent'Each node selected independently, no cascading
'leaf'Only leaf nodes can be selected; parent selection selects leaf descendants
ts
// Cascading checkbox tree
const tree = createNested({ selection: 'cascade' })

// Independent selection
const flat = createNested({ selection: 'independent' })

// Leaf-only selection (file picker)
const picker = createNested({ selection: 'leaf' })

Selection Modes

Cascade Mode (Default)

Selection propagates through the hierarchy:

Selecting a parent selects all descendants:

ts
tree.select('root')
// root, child-1, child-2, grandchild-1, etc. are all selected

Selecting a child updates ancestors to mixed state:

ts
tree.select('child-1')
// child-1 is selected
// root shows mixed state (some children selected)

Automatic state resolution:

  • All children selected → Parent becomes selected (not mixed)

  • Some children selected → Parent becomes mixed

  • No children selected → Parent becomes unselected (not mixed)

Independent Mode

Each node is selected independently with no cascading:

ts
const tree = createNested({ selection: 'independent' })

tree.select('parent')
// Only 'parent' is selected, children unchanged

Leaf Mode

Only leaf nodes can be selected. Selecting a parent selects all leaf descendants:

ts
const tree = createNested({ selection: 'leaf' })

tree.select('folder')
// All files (leaves) under 'folder' are selected
// 'folder' itself is not in selectedIds

Convenience Methods

Expand/Collapse All

ts
// Open all non-leaf nodes
tree.expandAll()

// Close all nodes
tree.collapseAll()

Data Transformation

Convert tree to flat array for serialization or API consumption:

ts
const flat = tree.toFlat()
// Returns: [{ id, parentId, value }, ...]

// Useful for sending to APIs or AI systems
console.log(JSON.stringify(flat))

Inline Children Registration

Define children directly when registering items:

ts
tree.onboard([
  {
    id: 'nav',
    value: 'Navigation',
    children: [
      { id: 'home', value: 'Home' },
      { id: 'about', value: 'About' },
      {
        id: 'products',
        value: 'Products',
        children: [
          { id: 'widgets', value: 'Widgets' },
          { id: 'gadgets', value: 'Gadgets' },
        ],
      },
    ],
  },
])

Cascade Unregister

Remove a node and optionally all its descendants:

ts
// Remove node, orphan children (default)
tree.unregister('parent')

// Remove node and all descendants
tree.unregister('parent', true)

// Batch removal with cascade
tree.offboard(['node-1', 'node-2'], true)

Ticket Properties

Each registered node receives additional properties:

ts
const node = tree.register({ id: 'node', value: 'Node', parentId: 'root' })

// Reactive refs
node.isOpen.value      // boolean - is this node open?
node.isLeaf.value      // boolean - has no children?
node.depth.value       // number - depth in tree (0 = root)

// Methods
node.open()            // Open this node
node.close()           // Close this node
node.flip()            // Flip open/closed state
node.getPath()         // Get path from root to this node
node.getAncestors()    // Get all ancestors
node.getDescendants()  // Get all descendants

Context Pattern

Use with Vue’s provide/inject for component trees:

ts
import { createNestedContext } from '@vuetify/v0'

// Create a trinity
const [useTree, provideTree, defaultTree] = createNestedContext({
  namespace: 'my-tree',
})

// In parent component
provideTree()

// In child components
const tree = useTree()

API Reference

The following API details are for the createNested composable.

Benchmarks

Every operation is profiled across multiple dataset sizes to measure real-world throughput. Each benchmark is assigned a performance tier—good, fast, blazing, or slow—and groups are scored by averaging their individual results so you can spot bottlenecks at a glance. This transparency helps you make informed decisions about which patterns scale for your use case. Learn more in the benchmarks guide.

View benchmark source↗

Was this page helpful?

Ctrl+/