Skip to content

Tree view

A standalone tree view component that renders a hierarchical list of nodes with expand/collapse support. Nodes emit click and dblclick events so the parent can react to user interaction. The tree is rendered inline (not in a popup) and includes connecting lines. Every node ends its guide line with a marker of the same size: nodes with children render it as the expand toggle holding the chevron, leaf nodes as a plain circle. Markers can be colored per node or per depth level.

Required icons

angle-right

Props

expanded-depth?: number
The number of levels that are visible without interaction. `1` shows the roots only, `2` the roots and their children, `Infinity` expands the whole tree. Nodes expanded or collapsed by the user keep their state until the options change.
Default: 1

level-colors?: (FluxColor | string)[]
An array of colors per depth level, used as a fallback for nodes that have no `color` of their own. Index 0 = root level, index 1 = first child level, etc. Each entry can be a `FluxColor` name (e.g. `primary`, `danger`) or any CSS color string (e.g. `#6366f1`). Levels without an entry keep the neutral marker.

options: FluxTreeViewOption[]
The tree structure of nodes to display.

Emits

click: [FluxTreeViewOption]
Triggered when a node is clicked.

dblclick: [FluxTreeViewOption]
Triggered when a node is double-clicked. Also toggles expand/collapse if the node has children.

Slots

trailing
Content rendered after the label of every node, e.g. a badge or action. Receives the flattened node via the `node` slot prop.

Option object

Each entry in the options array (and nested children arrays) is a FluxTreeViewOption:

Property
Type
Description
id
string | number
Unique identifier for the node. Included in emitted events.
label
string
Display label for the node.
icon
FluxIconName
Optional icon shown before the label.
color
FluxColor | string
Color of this node's marker, either a FluxColor name or any CSS color string. Takes precedence over level-colors.
disabled
boolean
Dims the node and stops it emitting click and dblclick. Its marker stays clickable, so a disabled branch can still be expanded.
children
FluxTreeViewOption[]
Nested child nodes. A node with children shows an expand/collapse chevron.

Keyboard navigation

Key
Action
Arrow Down
Move highlight to the next visible node.
Arrow Up
Move highlight to the previous visible node.
Arrow Right
Expand the highlighted node (if collapsed). If already expanded, moves to its first child.
Arrow Left
Collapse the highlighted node (if expanded). If already collapsed, moves to its parent.
Enter / Space
Emit click for the highlighted node.
Any letter
Jump to the first visible node whose label starts with that letter.

Accessibility

The tree is a single composite widget: the role="tree" container is the only tab stop and exposes the highlighted node through aria-activedescendant, while each node carries role="treeitem" with aria-level, aria-selected and (when it has children) aria-expanded. Parent → child grouping is conveyed with aria-owns rather than nested role="group" wrappers, so the flat DOM stays valid for assistive tech. Set a descriptive aria-label to name the tree.

The highlight is tracked by node identity: when the visible set changes (for example a node above the highlight collapses), the highlight stays on the same node instead of jumping to whatever now occupies that position.

Examples

Basic

A tree view with per-level colors.

<template>
    <FluxPane style="max-width: 390px">
        <FluxPaneBody>
            <FluxTreeView
                :level-colors="['primary', 'info', 'success']"
                :options="options"/>
        </FluxPaneBody>
    </FluxPane>
</template>

<script
    lang="ts"
    setup>
    import { FluxPane, FluxPaneBody, FluxTreeView } from '@flux-ui/components';
    import type { FluxTreeViewOption } from '@flux-ui/types';

    const options: FluxTreeViewOption[] = [
        {
            id: 1,
            label: 'Electronics',
            children: [
                {
                    id: 2,
                    label: 'Computers',
                    children: [
                        { id: 3, label: 'Laptops' },
                        { id: 4, label: 'Desktops' },
                        { id: 5, label: 'Tablets' }
                    ]
                },
                {
                    id: 6,
                    label: 'Phones',
                    children: [
                        { id: 7, label: 'Smartphones' },
                        { id: 8, label: 'Feature phones' }
                    ]
                }
            ]
        },
        {
            id: 9,
            label: 'Clothing',
            children: [
                { id: 10, label: 'Men' },
                { id: 11, label: 'Women' },
                { id: 12, label: 'Kids' }
            ]
        },
        {
            id: 13,
            label: 'Home & Garden',
            children: [
                { id: 14, label: 'Furniture' },
                { id: 15, label: 'Garden tools' }
            ]
        }
    ];
</script>

Expanded depth

With expanded-depth, the tree opens with more than just its roots visible. 2 shows the roots and their children.

Electronics
Clothing
Men
Women

<template>
    <FluxPane style="max-width: 390px">
        <FluxPaneBody>
            <FluxTreeView
                :expanded-depth="2"
                :level-colors="['primary', 'info', 'success']"
                :options="options"/>
        </FluxPaneBody>
    </FluxPane>
</template>

<script
    lang="ts"
    setup>
    import { FluxPane, FluxPaneBody, FluxTreeView } from '@flux-ui/components';
    import type { FluxTreeViewOption } from '@flux-ui/types';

    const options: FluxTreeViewOption[] = [
        {
            id: 1,
            label: 'Electronics',
            children: [
                {
                    id: 2,
                    label: 'Computers',
                    children: [
                        { id: 3, label: 'Laptops' },
                        { id: 4, label: 'Desktops' }
                    ]
                },
                {
                    id: 5,
                    label: 'Phones',
                    children: [
                        { id: 6, label: 'Smartphones' },
                        { id: 7, label: 'Feature phones' }
                    ]
                }
            ]
        },
        {
            id: 8,
            label: 'Clothing',
            children: [
                { id: 9, label: 'Men' },
                { id: 10, label: 'Women' }
            ]
        }
    ];
</script>

Events

Responding to click and double-click events.

<template>
    <FluxPane style="max-width: 390px">
        <FluxPaneBody>
            <FluxTreeView
                :level-colors="['primary', 'info', 'success']"
                :options="options"
                @click="onItemClick"
                @dblclick="onItemDblClick"/>
        </FluxPaneBody>
        <FluxPaneBody v-if="lastEvent">
            <p style="font-size: 14px; margin: 0;">
                <strong>{{ lastEvent.type }}</strong>: {{ lastEvent.label }}
                <span style="color: var(--foreground-secondary);">(id: {{ lastEvent.id }})</span>
            </p>
        </FluxPaneBody>
    </FluxPane>
</template>

<script
    lang="ts"
    setup>
    import { FluxPane, FluxPaneBody, FluxTreeView } from '@flux-ui/components';
    import type { FluxTreeViewOption } from '@flux-ui/types';
    import { ref } from 'vue';

    const lastEvent = ref<{ type: string; id: string | number; label: string } | null>(null);

    const options: FluxTreeViewOption[] = [
        {
            id: 1,
            label: 'Electronics',
            children: [
                { id: 2, label: 'Laptops' },
                { id: 3, label: 'Desktops' },
                { id: 4, label: 'Smartphones' }
            ]
        },
        {
            id: 5,
            label: 'Clothing',
            children: [
                { id: 6, label: 'Men' },
                { id: 7, label: 'Women' }
            ]
        }
    ];

    function onItemClick(option: FluxTreeViewOption): void {
        lastEvent.value = { type: 'click', id: option.id, label: option.label };
    }

    function onItemDblClick(option: FluxTreeViewOption): void {
        lastEvent.value = { type: 'dblclick', id: option.id, label: option.label };
    }
</script>

Trailing

Rendering a badge after each node label with the trailing slot.

<template>
    <FluxPane style="max-width: 390px">
        <FluxPaneBody>
            <FluxTreeView
                aria-label="Product categories"
                :level-colors="['primary', 'info', 'success']"
                :options="options">
                <template #trailing="{ node }">
                    <FluxBadge
                        v-if="counts[node.id]"
                        :label="`${counts[node.id]}`"/>
                </template>
            </FluxTreeView>
        </FluxPaneBody>
    </FluxPane>
</template>

<script
    lang="ts"
    setup>
    import { FluxBadge, FluxPane, FluxPaneBody, FluxTreeView } from '@flux-ui/components';
    import type { FluxTreeViewOption } from '@flux-ui/types';

    const counts: Record<string | number, number> = {
        3: 12,
        4: 8,
        5: 4,
        7: 21,
        8: 3
    };

    const options: FluxTreeViewOption[] = [
        {
            id: 1,
            label: 'Electronics',
            children: [
                {
                    id: 2,
                    label: 'Computers',
                    children: [
                        { id: 3, label: 'Laptops' },
                        { id: 4, label: 'Desktops' },
                        { id: 5, label: 'Tablets' }
                    ]
                },
                {
                    id: 6,
                    label: 'Phones',
                    children: [
                        { id: 7, label: 'Smartphones' },
                        { id: 8, label: 'Feature phones' }
                    ]
                }
            ]
        }
    ];
</script>

Used components