Skip to main content
Vuetify0 v1.0 is here
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

Treeview

A compound component for building accessible hierarchical tree interfaces with expand/collapse and selection support.

Edit this page
Report a Bug
Open issues
View on GitHub
Copy Markdown

PreviewRenders elementAdvancedJul 19, 2026

Usage

Display hierarchical data as an expandable, selectable tree. Nodes open and close, selection cascades through parents and children, and v-model tracks the selected nodes.

  •  Fruits
  •  Vegetables
  •  Bread
<script setup lang="ts">
  import { Treeview } from '@vuetify/v0'

  const items = [
    {
      name: 'Fruits',
      children: [
        { name: 'Apple' },
        { name: 'Banana' },
        { name: 'Orange' },
      ],
    },
    {
      name: 'Vegetables',
      children: [
        { name: 'Carrot' },
        { name: 'Broccoli' },
      ],
    },
    { name: 'Bread' },
  ]
</script>

<template>
  <Treeview.Root multiple>
    <Treeview.List class="text-sm text-on-surface select-none">
      <Treeview.Item
        v-for="item in items"
        :key="item.name"
        class="py-0.5"
        :value="item.name"
      >
        <div class="inline-flex items-center gap-1.5">
          <Treeview.Activator
            v-if="item.children"
            class="inline-flex items-center border-none bg-transparent p-0 cursor-pointer text-on-surface hover:text-primary"
          >
            <Treeview.Cue v-slot="{ attrs }" renderless>
              <svg
                v-bind="attrs"
                class="size-3.5 opacity-60 transition-transform data-[state=open]:rotate-90"
                fill="none"
                stroke="currentColor"
                viewBox="0 0 24 24"
              ><path d="M9 5l7 7-7 7" stroke-linecap="round" stroke-linejoin="round" stroke-width="2" /></svg>
            </Treeview.Cue>
          </Treeview.Activator>

          <span v-else class="inline-block w-3.5" />

          <Treeview.Checkbox v-slot="{ attrs, isSelected, isMixed }" renderless>
            <span
              v-bind="attrs"
              class="size-4 inline-flex items-center justify-center border rounded-sm text-xs leading-none cursor-pointer shrink-0"
              :class="isSelected || isMixed ? 'bg-primary text-on-primary border-primary' : 'border-on-surface/40'"
            >{{ isMixed ? '−' : isSelected ? '✓' : '\u00A0' }}</span>
          </Treeview.Checkbox>

          <span>{{ item.name }}</span>
        </div>

        <Treeview.Content v-if="item.children">
          <Treeview.Group class="pl-5">
            <Treeview.Item
              v-for="child in item.children"
              :key="child.name"
              class="py-0.5"
              :value="child.name"
            >
              <div class="inline-flex items-center gap-1.5">
                <span class="inline-block w-3.5" />

                <Treeview.Checkbox v-slot="{ attrs, isSelected }" renderless>
                  <span
                    v-bind="attrs"
                    class="size-4 inline-flex items-center justify-center border rounded-sm text-xs leading-none cursor-pointer shrink-0"
                    :class="isSelected ? 'bg-primary text-on-primary border-primary' : 'border-on-surface/40'"
                  >{{ isSelected ? '✓' : '\u00A0' }}</span>
                </Treeview.Checkbox>

                <span>{{ child.name }}</span>
              </div>
            </Treeview.Item>
          </Treeview.Group>
        </Treeview.Content>
      </Treeview.Item>
    </Treeview.List>
  </Treeview.Root>
</template>

Anatomy

vue
<script setup lang="ts">
  import { Treeview } from '@vuetify/v0'
</script>

<template>
  <Treeview.Root>
    <Treeview.List>
      <Treeview.Item>
        <Treeview.Activator>
          <Treeview.Cue />
        </Treeview.Activator>

        <Treeview.Checkbox>
          <Treeview.Indicator />
        </Treeview.Checkbox>

        <Treeview.Content>
          <Treeview.Group>
            <Treeview.Item />
          </Treeview.Group>
        </Treeview.Content>
      </Treeview.Item>
    </Treeview.List>
  </Treeview.Root>
</template>

Examples

Settings Panel

The Settings Panel demonstrates building a real-world tree UI from reactive data — categories with children that can be opened and closed, leaf nodes that activate a detail pane on click, and in-tree functional controls (toggles and selects) that modify the underlying data without leaving the tree.

SettingNode.vue handles both categories and leaves in a single recursive component: categories render a Treeview.Activator wrapping a chevron, while leaves render a <button> that calls activate() from the Item slot and emits upward. The --v0-treeview-depth CSS variable drives padding-left on each row, so indentation scales automatically with nesting depth — no manual level counting needed.

The “Experimental” category uses :disabled on Treeview.Item and is styled via [data-disabled] in scoped CSS. The active row is highlighted via [data-active] — the activate slot method sets this state independently of selection, making it suitable for single-item focus patterns like settings panels, file explorers, and inspector trees.

For trees where the primary interaction is multi-select (not activation), prefer cascade selection with Treeview.Checkbox and Treeview.Indicator — see the Cascade Selection recipe below.

FileRole
SettingNode.vueRecursive node component rendering categories and leaf settings
settings-panel.vueRoot tree with reactive settings data and a detail pane

Click a setting to see its description.

Recipes

Expansion Mode

Control how many nodes can be open at once with the open prop:

vue
<template>
  <!-- Default: multiple nodes can be open simultaneously -->
  <Treeview.Root open="multiple" />

  <!-- Accordion: only one node open at a time -->
  <Treeview.Root open="single" />
</template>

Use open-all to expand all nodes on mount:

vue
<template>
  <Treeview.Root open-all>
    <!-- All nodes start expanded -->
  </Treeview.Root>
</template>

Selection Mode

The selection prop controls how selection propagates through the hierarchy:

ValueBehavior
cascade (default)Selecting a parent selects all descendants; parents show tri-state when partially selected
independentEach node selected independently, no cascading
leafOnly leaf nodes can be selected; selecting a parent selects all its leaf descendants
vue
<template>
  <Treeview.Root v-model="selected" selection="leaf">
    <!-- Only leaf nodes appear in v-model -->
  </Treeview.Root>
</template>

Active Item

The active prop controls single vs. multi-highlight mode (independent of selection):

vue
<template>
  <!-- Default: only one item highlighted at a time -->
  <Treeview.Root active="single" />

  <!-- Multiple items can be highlighted simultaneously -->
  <Treeview.Root active="multiple" />
</template>

Reveal

Set reveal to automatically open all ancestor nodes when a descendant is opened. Useful for “navigate to item” patterns where a deep node is programmatically opened:

vue
<template>
  <Treeview.Root reveal>
    <!-- Opening a deep node opens its entire ancestor chain -->
  </Treeview.Root>
</template>

Cascade Selection

Add v-model to Treeview.Root for cascade selection. Use Treeview.Checkbox and Treeview.Indicator for tri-state checkboxes. Use Treeview.SelectAll for a tree-wide toggle.

vue
<script setup lang="ts">
  import { Treeview } from '@vuetify/v0'
  import { ref } from 'vue'

  const selected = ref<string[]>([])
</script>

<template>
  <Treeview.Root v-model="selected">
    <Treeview.SelectAll>
      <Treeview.Indicator />
      Select All
    </Treeview.SelectAll>

    <Treeview.List>
      <Treeview.Item value="users">
        <Treeview.Checkbox>
          <Treeview.Indicator />
        </Treeview.Checkbox>
        Users

        <Treeview.Group>
          <Treeview.Item value="users:view">
            <Treeview.Checkbox>
              <Treeview.Indicator />
            </Treeview.Checkbox>
            View
          </Treeview.Item>

          <Treeview.Item value="users:create">
            <Treeview.Checkbox>
              <Treeview.Indicator />
            </Treeview.Checkbox>
            Create
          </Treeview.Item>
        </Treeview.Group>
      </Treeview.Item>
    </Treeview.List>
  </Treeview.Root>
</template>

Styling with Data Attributes

All sub-components expose data attributes for CSS-driven state styling:

ComponentAttributeValues
Itemdata-selectedPresent when selected
Itemdata-disabledPresent when disabled
Itemdata-openPresent when expanded
Itemdata-activePresent when active
Activatordata-disabledPresent when disabled
Activatordata-openPresent when expanded
Checkboxdata-selectedPresent when checked
Checkboxdata-disabledPresent when disabled
Checkboxdata-mixedPresent when indeterminate
Cuedata-stateopen or closed
Indicatordata-statechecked, unchecked, or indeterminate

The --v0-treeview-depth CSS variable is set on each Item, enabling indentation:

css
.tree-item {
  padding-left: calc(var(--v0-treeview-depth) * 1rem);
}

Accessibility

Treeview implements the WAI-ARIA Tree View pattern↗︎. Treeview.List establishes roving tabindex, so only one node is in the Tab order at a time and the arrow keys move focus between nodes. The Activator and Checkbox are tabindex="-1" and are reached through the tree rather than the page’s Tab sequence.

ARIA Attributes

AttributeValueElement
roletreeList
aria-multiselectabletrue / falseList
aria-labelProvided labelList
rolegroupGroup
roletreeitemItem
aria-expandedtrue / false (only when the node has children)Item
aria-selectedtrue / falseItem
aria-disabledtrue / falseItem
aria-levelDepth (1-based)Item
aria-posinsetPosition among siblingsItem
aria-setsizeSibling countItem
aria-currenttrue when activeItem
rolecheckboxCheckbox, SelectAll
aria-checkedtrue / false / mixedCheckbox, SelectAll
aria-hiddentrueCue, Indicator

Keyboard Navigation

KeyAction
ArrowUpMoves focus to the previous visible node
ArrowDownMoves focus to the next visible node
ArrowRightExpands a collapsed node, or moves focus to its first child
ArrowLeftCollapses an expanded node, or moves focus to its parent
HomeMoves focus to the first node
EndMoves focus to the last visible node
EnterToggles expansion (when expandable) and activates the node
SpaceToggles selection of the focused node
*Expands all sibling nodes at the current level
TabMoves focus to focusable controls inside a row, then out to the next node

In RTL, the ArrowRight and ArrowLeft directions are swapped. Treeview.SelectAll toggles the whole tree with Enter or Space.

FAQ

Discord
Need help? Join our community for support and discussions ↗
Was this page helpful?

© 2016-1970 Vuetify, LLC
Services
Ctrl+/