---
title: "Tree Data"
description: "Enable tree-mode tables via childrenKey — configure expansion, cascade selection strategies, indentation and read selection results."
canonical_url: "https://nuxt.mhaibaraai.cn/en/docs/data-table/tree"
---
# Tree Data

> Enable tree-mode tables via childrenKey — configure expansion, cascade selection strategies, indentation and read selection results.

## Tree Data Basics

Setting `children-key` enables tree mode. Combine with `row-key` to derive row ids and an `expand` column to render collapse buttons. Missing fields on parent rows (e.g. level) can be filled by `emptyCell`:

```vue [DataTableTreeBasicExample.vue]
<script setup lang="ts">
import type { DataTableColumn } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 2)

const columns: DataTableColumn<Person>[] = [
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' },
  { accessorKey: 'level', header: '职级', emptyCell: '—' }
]
</script>

<template>
  <MDataTable :data="treeData" :columns="columns" children-key="children" row-key="id" :ui="{ root: 'max-h-[50vh]' }" />
</template>
```

## `strategy` Cascade Selection Strategy

The `strategy` of the `selection` column determines parent–child check synchronization: `cascade` for parent–child cascade, `isolated` for independent selection, `leaf` for leaf-only selection (parent nodes show a derived state from their descendants):

```vue [DataTableTreeStrategyExample.vue]
<script setup lang="ts">
import type { DataTableColumn, DataTableSelectionColumn } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 2)
const strategy = ref<DataTableSelectionColumn['strategy']>('cascade')

const columns = computed<DataTableColumn<Person>[]>(() => [
  { type: 'selection', strategy: strategy.value },
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' }
])
</script>

<template>
  <div class="flex flex-col gap-3">
    <USelect
      v-model="strategy"
      :items="[
        { label: 'cascade 父子级联', value: 'cascade' },
        { label: 'isolated 父子独立', value: 'isolated' },
        { label: 'leaf 仅叶子可勾', value: 'leaf' }
      ]"
      value-key="value"
      size="xs"
      class="w-56"
    />
    <MDataTable :data="treeData" :columns="columns" children-key="children" row-key="id" />
  </div>
</template>
```

## `mode` Single and Multiple Selection

With `mode: 'single'`, the entire tree allows only one row to be checked at a time (usually paired with `isolated`); with `multiple`, the `strategy` drives the cascade behavior:

```vue [DataTableTreeModeExample.vue]
<script setup lang="ts">
import type { DataTableColumn, DataTableSelectionColumn } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 2)
const mode = ref<DataTableSelectionColumn['mode']>('multiple')

const columns = computed<DataTableColumn<Person>[]>(() => [
  { type: 'selection', mode: mode.value, strategy: mode.value === 'single' ? 'isolated' : 'cascade' },
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' }
])
</script>

<template>
  <div class="flex flex-col gap-3">
    <USwitch
      :model-value="mode === 'single'"
      label="single 单选"
      @update:model-value="mode = $event ? 'single' : 'multiple'"
    />
    <MDataTable :data="treeData" :columns="columns" children-key="children" row-key="id" />
  </div>
</template>
```

## Reading Selection Results

`v-model:row-selection-keys` bi-directionally syncs the selected id array; the component-exposed `treeSelection` derives four business views — `leaves`, `parents`, `halfSelected` and `strictlyChecked` — and provides `clearSelection()`:

```vue [DataTableTreeResultExample.vue]
<script setup lang="ts">
import type { DataTableColumn, DataTableExposed } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 2)
const tableRef = useTemplateRef<DataTableExposed<Person>>('tableRef')
const selectionKeys = ref<string[]>([])
const names = (rows: Person[]) => rows.map(p => p.name)

const result = computed(() => {
  const r = tableRef.value?.treeSelection
  if (!r) return {}
  return {
    selectionKeys: selectionKeys.value,
    leaves: names(r.leaves),
    parents: names(r.parents),
    halfSelected: names(r.halfSelected),
    strictlyChecked: names(r.strictlyChecked)
  }
})

const columns: DataTableColumn<Person>[] = [
  { type: 'selection', strategy: 'cascade' },
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' }
]
</script>

<template>
  <div class="flex flex-col gap-3">
    <UButton size="xs" variant="soft" class="self-start" @click="tableRef?.clearSelection()">
      清空选中
    </UButton>
    <MDataTable
      ref="tableRef"
      v-model:row-selection-keys="selectionKeys"
      :data="treeData"
      :columns="columns"
      children-key="children"
      row-key="id"
    />
    <pre class="text-xs p-3 rounded-md bg-muted overflow-auto">{{ result }}</pre>
  </div>
</template>
```

## `indentSize` Tree Indentation

`indentSize` supports string, number (px) and function forms; the function form can dynamically adjust based on `row.depth` and other context:

```vue [DataTableTreeIndentExample.vue]
<script setup lang="ts">
import type { DataTableColumn } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 3)

const columns: DataTableColumn<Person>[] = [
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' }
]
</script>

<template>
  <MDataTable :default-expanded="1" :data="treeData" :columns="columns" children-key="children" row-key="id" />
</template>
```

## `defaultExpanded` Default Expansion

`default-expanded` determines the initial expansion state when neither `expanded` nor `expanded-keys` (controlled mode takes priority) is provided: `true` expands all parent rows; a `number` expands parent rows whose `depth` is less than that value (equivalent to `expandToDepth(n)`); a function `(row, depth) => boolean` customizes expansion per row and depth, without having to manually collect parent ids:

```vue [DataTableTreeDefaultExpandExample.vue]
<script setup lang="ts">
import type { DataTableColumn, DataTableProps } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const props = withDefaults(defineProps<{
  mode?: 'all' | 'depth1' | 'depth2' | 'fn'
}>(), { mode: 'all' })

const treeData = makePeopleTree(3, 3, 3)

const columns: DataTableColumn<Person>[] = [
  { type: 'expand' },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' }
]

const defaultExpanded = computed<DataTableProps<Person>['defaultExpanded']>(() => {
  if (props.mode === 'depth1') return 1
  if (props.mode === 'depth2') return 2
  if (props.mode === 'fn') return row => row.department === '设计'
  return true
})
</script>

<template>
  <MDataTable
    :key="props.mode"
    :default-expanded="defaultExpanded"
    :data="treeData"
    :columns="columns"
    children-key="children"
    row-key="id"
    class="max-h-150"
  />
</template>
```

## Expansion Behavior Control

The `expand` column's `buttonProps` and `toggleAllButtonProps` customize the cell and header buttons respectively (receiving `isExpanded` / `isAllExpanded`); `expand-on-row-click` triggers expansion on a full-row click; the exposed `expandToDepth(n)` expands by layer and `collapseAll()` collapses everything; `v-model:expanded-keys` syncs the expanded id array:

```vue [DataTableTreeExpandExample.vue]
<script setup lang="ts">
import type { DataTableColumn, DataTableExposed } from '@movk/nuxt'
import type { Person } from '~/composables/useTableMock'

const treeData = makePeopleTree(3, 3, 3)
const tableRef = useTemplateRef<DataTableExposed<Person>>('tableRef')
const expandOnRowClick = ref(false)

const columns: DataTableColumn<Person>[] = [
  {
    type: 'expand',
    buttonProps: ctx => ({
      icon: ctx.isExpanded ? 'i-lucide-folder-open' : 'i-lucide-folder',
      color: ctx.isExpanded ? 'primary' : 'neutral',
      variant: 'soft'
    }),
    toggleAllButtonProps: ctx => ({
      icon: ctx.isAllExpanded ? 'i-lucide-chevrons-down-up' : 'i-lucide-chevrons-up-down',
      variant: 'soft',
      color: 'primary'
    })
  },
  { accessorKey: 'name', header: '成员' },
  { accessorKey: 'department', header: '部门' },
  { accessorKey: 'role', header: '岗位' }
]
</script>

<template>
  <div class="flex flex-col gap-3">
    <div class="flex flex-wrap items-center gap-2">
      <USwitch v-model="expandOnRowClick" label="expandOnRowClick" />
      <UButton size="xs" variant="soft" @click="tableRef?.expandToDepth(1)">
        展开 1 级
      </UButton>
      <UButton size="xs" variant="soft" @click="tableRef?.expandToDepth(2)">
        展开 2 级
      </UButton>
      <UButton size="xs" variant="soft" @click="tableRef?.collapseAll()">
        收起全部
      </UButton>
    </div>
    <MDataTable
      ref="tableRef"
      :data="treeData"
      :columns="columns"
      children-key="children"
      row-key="id"
      :expand-on-row-click="expandOnRowClick"
    />
  </div>
</template>
```


## Sitemap

See the full [sitemap](https://nuxt.mhaibaraai.cn/sitemap.md) for all pages.
