Tree view select
This is a form select element that displays options in a hierarchical tree structure. It supports expanding and collapsing branches, single and multiple selection, an optional search input for filtering, and cascading selection where picking a parent covers its whole subtree. Each selectable option carries a checkbox (multiple) or radio (single) showing whether it is selected. The tree is rendered with connecting lines to visualize the hierarchy. Every option ends its guide line with a marker of the same size: options with children render it as the expand toggle holding the chevron, leaf options as a plain circle. Markers can be colored per option or per depth level, using either a FluxColor name or any CSS/HEX color string.
Accessibility
The trigger is exposed as a role="combobox" (aria-haspopup="tree") and the option list as a role="listbox". The highlighted node is tracked through aria-activedescendant, selectable nodes carry role="option" with aria-selected, and non-selectable group headers are marked role="presentation". The option row itself stays the control: its checkbox or radio is a read-only indicator marked aria-hidden, so assistive tech announces the selection once, through aria-selected. When wrapped in a required Form field the trigger also receives aria-required.
Required icons
Props
model-value: FluxFormTreeViewSelectValue
The selected value(s) of the tree view select.
auto-focus?: boolean
Focus the trigger when the form is mounted.
disabled?: boolean
If the tree view select is disabled.
error?: string | null
Error message describing why the tree view select is invalid. Sets aria-invalid and a red border.
expanded-depth?: number
The number of levels that are visible without interaction whenever the popup opens. `1` shows the roots only, `2` the roots and their children, `Infinity` expands the whole tree. Ancestors of the current selection are expanded on top of this, and a manual collapse keeps working while the popup stays open.
Default: 1
is-cascading?: boolean
If selecting a node implicitly covers its whole subtree. Descendants of a selected node are then shown as checked and locked. Only applies together with `is-multiple`. The model value still holds only the explicitly selected ids.
is-loading?: boolean
Marks the tree view select as loading.
is-multiple?: boolean
If the tree view select allows multiple values to be selected.
is-readonly?: boolean
If the tree view select is readonly. Blocks opening the popup.
is-searchable?: boolean
If the tree view select has a search input to filter options.
is-secondary?: boolean
If the field is secondary and is rendered in an alternative style.
name?: string
The name attribute passed to the underlying form control.
level-colors?: (FluxColor | string)[]
An array of colors per depth level, used as a fallback for options 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: FluxFormTreeViewSelectOption[]
The tree structure of options to display.
placeholder?: string
The placeholder text to display when no value is selected.
Emits
update:model-value: [FluxFormTreeViewSelectValue]
Triggered when the selected value changes.
Option object
Each entry in the options array (and nested children arrays) is a FluxFormTreeViewSelectOption:
idstring | numberUnique identifier used as the value when selected.
labelstringDisplay label for the node.
iconFluxIconNameOptional icon shown before the label.
colorFluxColor | stringColor of this option's marker, either a FluxColor name or any CSS color string. Takes precedence over level-colors.
disabledbooleanDims the option and blocks selecting it. Its marker stays clickable, so a disabled branch can still be expanded.
selectablebooleanWhether this node can be selected. Defaults to true. Set to false on parent nodes to make them act as group headers. Clicking them only expands or collapses their children.
childrenFluxFormTreeViewSelectOption[]Nested child options. A node with children shows an expand/collapse chevron.
Keyboard navigation
Open the popup (when closed) or select/activate the highlighted node.
Move highlight to the next visible node.
Move highlight to the previous visible node.
Expand the highlighted node (if collapsed). If already expanded, moves to its first child.
Collapse the highlighted node (if expanded). If already collapsed, moves to its parent.
(multiple mode) Removes the last selected tag when the search field is empty.
Close the popup and return focus to the trigger.
Close the popup.
Jump to the first visible node whose label starts with that letter.
Examples
Basic
A tree view select with per-level colors.
<template>
<FluxPane style="max-width: 390px">
<FluxPaneBody>
<FluxFormField label="Category">
<FluxFormTreeViewSelect
v-model="selectedValue"
:level-colors="['primary', 'info', 'success']"
:options="options"
placeholder="Select a category..."/>
</FluxFormField>
</FluxPaneBody>
</FluxPane>
</template>
<script
lang="ts"
setup>
import { FluxFormField, FluxFormTreeViewSelect, FluxPane, FluxPaneBody } from '@flux-ui/components';
import { ref } from 'vue';
const selectedValue = ref<number | null>(null);
const options = [
{
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>Multiple
A tree view select that allows selecting multiple items across the tree.
<template>
<FluxPane style="max-width: 390px">
<FluxPaneBody>
<FluxFormField label="Categories">
<FluxFormTreeViewSelect
v-model="selectedValues"
:level-colors="['primary', 'info', 'success']"
:options="options"
is-multiple
placeholder="Select categories..."/>
</FluxFormField>
</FluxPaneBody>
</FluxPane>
</template>
<script
lang="ts"
setup>
import { FluxFormField, FluxFormTreeViewSelect, FluxPane, FluxPaneBody } from '@flux-ui/components';
import { ref } from 'vue';
const selectedValues = ref<number[]>([]);
const options = [
{
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>Searchable
A tree view select with a search input. Matching nodes keep their place in the hierarchy: their ancestors stay visible and non-matching branches are pruned.
<template>
<FluxPane style="max-width: 390px">
<FluxPaneBody>
<FluxFormField label="Category">
<FluxFormTreeViewSelect
v-model="selectedValue"
:level-colors="['primary', 'info', 'success']"
:options="options"
is-searchable
placeholder="Search and select..."/>
</FluxFormField>
</FluxPaneBody>
</FluxPane>
</template>
<script
lang="ts"
setup>
import { FluxFormField, FluxFormTreeViewSelect, FluxPane, FluxPaneBody } from '@flux-ui/components';
import { ref } from 'vue';
const selectedValue = ref<number | null>(null);
const options = [
{
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>Non-selectable parents
Parent nodes can have selectable: false so they act as group headers. Only leaf nodes can be selected.
<template>
<FluxPane style="max-width: 390px">
<FluxPaneBody>
<FluxFormField label="Product">
<FluxFormTreeViewSelect
v-model="selectedValue"
:level-colors="['gray', 'primary']"
:options="options"
placeholder="Select a product..."/>
</FluxFormField>
</FluxPaneBody>
</FluxPane>
</template>
<script
lang="ts"
setup>
import { FluxFormField, FluxFormTreeViewSelect, FluxPane, FluxPaneBody } from '@flux-ui/components';
import { ref } from 'vue';
const selectedValue = ref<number | null>(null);
// Parent nodes have `selectable: false`; only leaves can be selected.
const options = [
{
id: 1,
label: 'Electronics',
selectable: false,
children: [
{ id: 2, label: 'MacBook Pro 14"' },
{ id: 3, label: 'MacBook Air 13"' },
{ id: 4, label: 'iPhone 16 Pro' }
]
},
{
id: 5,
label: 'Clothing',
selectable: false,
children: [
{ id: 6, label: 'Classic T-shirt' },
{ id: 7, label: 'Slim-fit Jeans' }
]
},
{
id: 8,
label: 'Home & Garden',
selectable: false,
children: [
{ id: 9, label: 'Desk Lamp' },
{ id: 10, label: 'Bookshelf' }
]
}
];
</script>Expanded depth
With expanded-depth, the popup opens with more than just its roots visible. Handy when the top level only groups the options that can actually be picked.
<template>
<FluxPane style="max-width: 390px">
<FluxPaneBody>
<FluxFormField label="Boards">
<FluxFormTreeViewSelect
v-model="selectedValues"
:expanded-depth="2"
is-multiple
:level-colors="['gray', 'primary']"
:options="options"
placeholder="Select boards..."/>
</FluxFormField>
</FluxPaneBody>
</FluxPane>
</template>
<script
lang="ts"
setup>
import { FluxFormField, FluxFormTreeViewSelect, FluxPane, FluxPaneBody } from '@flux-ui/components';
import { ref } from 'vue';
const selectedValues = ref<number[]>([]);
// The root is a grouping level, so it is opened by default and only the boards can be picked.
const options = [
{
id: 1,
label: 'Central Pool',
selectable: false,
children: [
{
id: 2,
label: 'Aurora Foundation',
children: [
{ id: 3, label: 'Aurora College' },
{ id: 4, label: 'Aurora Primary' }
]
},
{
id: 5,
label: 'Meridian Education',
children: [
{ id: 6, label: 'Meridian Lyceum' }
]
},
{
id: 7,
label: 'Northwind Schools',
children: [
{ id: 8, label: 'Northwind Academy' },
{ id: 9, label: 'Northwind Primary' }
]
}
]
}
];
</script>Cascading
With is-cascading, selecting a parent covers its whole subtree. Its descendants are shown as checked and locked, while the model value keeps only the explicitly selected ids.
<template>
<FluxPane style="max-width: 390px">
<FluxPaneBody>
<FluxFormField label="Locations">
<FluxFormTreeViewSelect
v-model="selectedValues"
:options="options"
is-cascading
is-multiple
placeholder="Select locations..."/>
</FluxFormField>
</FluxPaneBody>
</FluxPane>
</template>
<script
lang="ts"
setup>
import { FluxFormField, FluxFormTreeViewSelect, FluxPane, FluxPaneBody } from '@flux-ui/components';
import { ref } from 'vue';
const selectedValues = ref<number[]>([]);
const options = [
{
id: 1,
label: 'Netherlands',
color: 'primary',
children: [
{
id: 2,
label: 'North Holland',
color: 'info',
children: [
{ id: 3, label: 'Amsterdam' },
{ id: 4, label: 'Haarlem' }
]
},
{
id: 5,
label: 'Utrecht',
color: 'info',
children: [
{ id: 6, label: 'Utrecht' },
{ id: 7, label: 'Amersfoort' }
]
}
]
},
{
id: 8,
label: 'Belgium',
color: 'primary',
children: [
{
id: 9,
label: 'Flanders',
color: 'info',
children: [
{ id: 10, label: 'Antwerp' },
{ id: 11, label: 'Ghent' }
]
}
]
}
];
</script>Disabled options
Options with disabled: true cannot be selected, but their branch can still be expanded.
<template>
<FluxPane style="max-width: 390px">
<FluxPaneBody>
<FluxFormField label="Category">
<FluxFormTreeViewSelect
v-model="selectedValue"
:level-colors="['primary', 'info', 'success']"
:options="options"
placeholder="Select a category..."/>
</FluxFormField>
</FluxPaneBody>
</FluxPane>
</template>
<script
lang="ts"
setup>
import { FluxFormField, FluxFormTreeViewSelect, FluxPane, FluxPaneBody } from '@flux-ui/components';
import { ref } from 'vue';
const selectedValue = ref<number | null>(null);
const options = [
{
id: 1,
label: 'Electronics',
children: [
{
id: 2,
label: 'Computers',
children: [
{ id: 3, label: 'Laptops' },
{ id: 4, label: 'Desktops', disabled: true },
{ id: 5, label: 'Tablets' }
]
},
{
id: 6,
label: 'Phones',
disabled: true,
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', disabled: true }
]
}
];
</script>Per-option colors
Coloring each option individually with color, instead of per depth level.
<template>
<FluxPane style="max-width: 390px">
<FluxPaneBody>
<FluxFormField label="Team">
<FluxFormTreeViewSelect
v-model="selectedValue"
:options="options"
placeholder="Select a team..."/>
</FluxFormField>
</FluxPaneBody>
</FluxPane>
</template>
<script
lang="ts"
setup>
import { FluxFormField, FluxFormTreeViewSelect, FluxPane, FluxPaneBody } from '@flux-ui/components';
import { ref } from 'vue';
const selectedValue = ref<number | null>(null);
// A color can be a FluxColor name or any CSS color string, and is set per option.
const options = [
{
id: 1,
label: 'Engineering',
color: 'primary',
children: [
{ id: 2, label: 'Frontend', color: '#6366f1' },
{ id: 3, label: 'Backend', color: '#0ea5e9' },
{ id: 4, label: 'Infrastructure', color: '#14b8a6' }
]
},
{
id: 5,
label: 'Design',
color: 'warning',
children: [
{ id: 6, label: 'Product design', color: '#f59e0b' },
{ id: 7, label: 'Brand', color: '#ec4899' }
]
},
{
id: 8,
label: 'Operations',
color: 'success',
children: [
{ id: 9, label: 'Support', color: '#10b981' },
{ id: 10, label: 'Finance', color: '#84cc16' }
]
}
];
</script>Custom colors
Using HEX colors instead of FluxColor names for each level.
<template>
<FluxPane style="max-width: 390px">
<FluxPaneBody>
<FluxFormField label="Category">
<FluxFormTreeViewSelect
v-model="selectedValue"
:level-colors="['#6366f1', '#f59e0b', '#10b981']"
:options="options"
placeholder="Select a category..."/>
</FluxFormField>
</FluxPaneBody>
</FluxPane>
</template>
<script
lang="ts"
setup>
import { FluxFormField, FluxFormTreeViewSelect, FluxPane, FluxPaneBody } from '@flux-ui/components';
import { ref } from 'vue';
const selectedValue = ref<number | null>(null);
const options = [
{
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: 'Clothing',
children: [
{ id: 8, label: 'Men' },
{ id: 9, label: 'Women' }
]
}
];
</script>