Group
A headless multi-selection component that provides the foundation for building custom multi-select interfaces. The Group component manages selection state and provides context to child items without imposing any specific UI.
Features
Multi-selection : Select multiple items simultaneously
Tri-state support : Items support selected, unselected, and mixed/indeterminate states
Batch operations : selectAll, unselectAll, toggleAll methods
Flexible constraints : Optional enrollment, mandatory selection modes
Renderless : No default UI, complete styling control
Type-safe : Full TypeScript support with generic value types
Basic Usage
Basic
With Batch Operations
< script setup lang = "ts" >
import { Group } from '@vuetify/v0'
import { ref } from 'vue'
const selected = ref < string []>([])
</ script >
< template >
< Group.Root v-model = " selected " >
< Group.Item value = "typescript" v-slot = " { attrs , isSelected } " >
< button v-bind = " attrs " : class = " { selected: isSelected } " >
TypeScript
</ button >
</ Group.Item >
< Group.Item value = "vue" v-slot = " { attrs , isSelected } " >
< button v-bind = " attrs " : class = " { selected: isSelected } " >
Vue
</ button >
</ Group.Item >
< Group.Item value = "vite" v-slot = " { attrs , isSelected } " >
< button v-bind = " attrs " : class = " { selected: isSelected } " >
Vite
</ button >
</ Group.Item >
</ Group.Root >
</ template >
Components
Group.Root
Root component that manages multi-selection state and provides context to child items.
Disables all items in the group
Auto-select all non-disabled items on mount
mandatory
boolean | 'force'
default: "false"
false: No constraints (items can be freely selected/deselected)
true: Prevents deselecting the last selected item
'force': Auto-selects first non-disabled item on mount
namespace
string
default: "'v0:group'"
Context namespace for dependency injection (must match GroupItem namespace)
Slot Props
Whether the group is disabled
Whether no items are selected
Whether all selectable (non-disabled) items are selected
Whether some but not all items are selected
Select one or more items by ID
Unselect one or more items by ID
Toggle selection state of one or more items by ID
Select all non-disabled items
Unselect all items (respects mandatory setting)
Toggle between all selected and none selected
Contains aria-multiselectable="true"
Group.Item
Represents a selectable item within the group. Automatically registers with parent and unregisters on unmount.
Value associated with this item
Unique identifier (auto-generated if not provided)
Optional display label (passed to slot props)
disabled
boolean | Ref<boolean>
default: "false"
Disables this specific item
indeterminate
boolean | Ref<boolean>
default: "false"
Sets this item to indeterminate/mixed state
namespace
string
default: "'v0:group'"
Context namespace (must match Group.Root namespace)
Slot Props
Whether this item is selected
Whether this item is in indeterminate state
Whether this item is disabled (own disabled state OR group disabled)
Toggle this item’s selection state
Set this item to indeterminate state
Clear indeterminate state
Pre-computed attributes:
role="checkbox"
aria-checked: boolean or "mixed"
aria-disabled: boolean
data-selected, data-disabled, data-mixed
Advanced Examples
Tag Selector
< script setup lang = "ts" >
import { Group } from '@vuetify/v0'
import { ref } from 'vue'
const tags = [ 'bug' , 'feature' , 'enhancement' , 'documentation' ]
const selectedTags = ref < string []>([ 'bug' ])
</ script >
< template >
< div class = "tag-selector" >
< Group.Root v-model = " selectedTags " >
< Group.Item
v-for = " tag in tags "
: key = " tag "
: value = " tag "
v-slot = " { attrs , isSelected , toggle } "
>
< button
v-bind = " attrs "
@ click = " toggle "
class = "tag"
: class = " { selected: isSelected } "
>
{{ tag }}
</ button >
</ Group.Item >
</ Group.Root >
</ div >
</ template >
< style >
.tag {
padding : 4 px 12 px ;
border-radius : 16 px ;
border : 1 px solid #ccc ;
background : white ;
cursor : pointer ;
}
.tag.selected {
background : #4CAF50 ;
color : white ;
border-color : #4CAF50 ;
}
</ style >
Hierarchical Selection with Indeterminate
< script setup lang = "ts" >
import { Group } from '@vuetify/v0'
import { ref , computed } from 'vue'
const childrenA = ref < string []>([])
const childrenB = ref < string []>([])
const parentAState = computed (() => {
if ( childrenA . value . length === 0 ) return false
if ( childrenA . value . length === 2 ) return true
return 'mixed'
})
</ script >
< template >
< div class = "hierarchy" >
<!-- Parent A -->
< Group.Item
value = "parent-a"
: indeterminate = " parentAState === 'mixed' "
v-slot = " { attrs , isMixed , isSelected , toggle } "
>
< button v-bind = " attrs " @ click = " toggle " >
< span v-if = " isMixed " > − </ span >
< span v-else-if = " isSelected " > ✓ </ span >
< span v-else > ☐ </ span >
Parent A
</ button >
</ Group.Item >
<!-- Children of A -->
< div class = "children" >
< Group.Root v-model = " childrenA " >
< Group.Item value = "a1" v-slot = " { attrs , isSelected } " >
< button v-bind = " attrs " >
< span v-if = " isSelected " > ✓ </ span >
< span v-else > ☐ </ span >
Child A1
</ button >
</ Group.Item >
< Group.Item value = "a2" v-slot = " { attrs , isSelected } " >
< button v-bind = " attrs " >
< span v-if = " isSelected " > ✓ </ span >
< span v-else > ☐ </ span >
Child A2
</ button >
</ Group.Item >
</ Group.Root >
</ div >
</ div >
</ template >
Filter Selection with Counters
< script setup lang = "ts" >
import { Group } from '@vuetify/v0'
import { ref , computed } from 'vue'
const filters = ref < string []>([])
const filterOptions = [
{ value: 'in-stock' , label: 'In Stock' , count: 42 },
{ value: 'on-sale' , label: 'On Sale' , count: 18 },
{ value: 'new' , label: 'New Arrivals' , count: 7 },
{ value: 'featured' , label: 'Featured' , count: 12 },
]
const activeFilterCount = computed (() => filters . value . length )
</ script >
< template >
< div class = "filter-panel" >
< div class = "filter-header" >
< h3 > Filters </ h3 >
< span class = "badge" v-if = " activeFilterCount > 0 " >
{{ activeFilterCount }} active
</ span >
</ div >
< Group.Root v-model = " filters " v-slot = " { unselectAll } " >
< button v-if = " activeFilterCount > 0 " @ click = " unselectAll " class = "clear-btn" >
Clear all
</ button >
< Group.Item
v-for = " option in filterOptions "
: key = " option . value "
: value = " option . value "
v-slot = " { attrs , isSelected , toggle } "
>
< button
v-bind = " attrs "
@ click = " toggle "
class = "filter-option"
: class = " { active: isSelected } "
>
< span class = "checkbox" >
< span v-if = " isSelected " > ✓ </ span >
</ span >
< span class = "label" > {{ option . label }} </ span >
< span class = "count" > {{ option . count }} </ span >
</ button >
</ Group.Item >
</ Group.Root >
</ div >
</ template >
Mandatory Selection
< script setup lang = "ts" >
import { Group } from '@vuetify/v0'
import { ref } from 'vue'
const required = ref < string []>([])
</ script >
< template >
< Group.Root v-model = " required " mandatory = "force" >
< p > At least one option must be selected: </ p >
< Group.Item value = "email" v-slot = " { attrs , isSelected } " >
< label >
< input type = "checkbox" v-bind = " attrs " : checked = " isSelected " />
Email notifications
</ label >
</ Group.Item >
< Group.Item value = "sms" v-slot = " { attrs , isSelected } " >
< label >
< input type = "checkbox" v-bind = " attrs " : checked = " isSelected " />
SMS notifications
</ label >
</ Group.Item >
< Group.Item value = "push" v-slot = " { attrs , isSelected } " >
< label >
< input type = "checkbox" v-bind = " attrs " : checked = " isSelected " />
Push notifications
</ label >
</ Group.Item >
</ Group.Root >
</ template >
Use Cases
When to Use Group
Custom multi-select UI : When Checkbox component styling is too constraining
Tag selectors : Selecting multiple tags or categories
Filter panels : Multi-select filters for data tables or search results
Pill selectors : Chip-like selection interfaces
Custom checkbox groups : When you need full control over checkbox appearance
When to Use Alternatives
Component Use When Checkbox You want standard checkbox UI with minimal styling Selection You need to switch between single and multi-select modes Single You only need single-selection (mutually exclusive options)
Accessibility
Items render with role="checkbox" and aria-checked by default
Supports aria-disabled for disabled items
Group includes aria-multiselectable="true"
No keyboard navigation built-in (add via your own event handlers)
The Group component is renderless and provides ARIA attributes via slot props. You’re responsible for binding these attributes and implementing keyboard navigation if needed.
Type Safety
Full TypeScript support with generic value types:
import type {
GroupRootProps ,
GroupItemProps ,
GroupRootSlotProps ,
GroupItemSlotProps
} from '@vuetify/v0'
// Type-safe with specific value types
const selected = ref < string []>([])
Comparison with Checkbox
Feature Group Checkbox UI Fully custom, renderless Structured with Root/Indicator Styling Complete control Semantic checkbox structure Form integration Manual Automatic with name prop Use case Custom interfaces Standard form checkboxes Complexity Lower-level primitive Higher-level component
Use Group when building custom selection UIs. Use Checkbox for standard form checkboxes.