createGroup
The createGroup composable is designed to manage a group of related components, allowing for shared state and behavior across them. It supports tri-state (mixed/indeterminate) for checkbox trees and similar use cases where items can be selected, unselected, or in a mixed state.
Usage
The createGroup composable manages a group of selectable items, letting you work with both their IDs and their position indexes. It supports selecting, unselecting, toggling, and reading the indexes of selected items.
import { createGroup } from '@vuetify/v0'
// Instantiate group
const group = createGroup()
// Register items
group.register({ id: 'apple', value: 'Apple' })
group.register({ id: 'banana', value: 'Banana' })
group.register({ id: 'cherry', value: 'Cherry' })
group.register({ id: 'date', value: 'Date' })
// Select some items
group.select(['apple', 'banana'])
console.log(group.selectedIndexes.value) // Set { 0, 1 }
// Toggle an item (banana will become unselected)
group.toggle('banana')
console.log(group.selectedIndexes.value) // Set { 0 }
// Unselect apple
group.unselect('apple')
console.log(group.selectedIndexes.value) // Set {}Examples
Showing all:
<script setup lang="ts">
import { createGroup } from '@vuetify/v0'
import { computed } from 'vue'
const tags = [
{ id: 'vue', label: 'Vue', color: 'bg-emerald-500' },
{ id: 'react', label: 'React', color: 'bg-sky-500' },
{ id: 'svelte', label: 'Svelte', color: 'bg-orange-500' },
{ id: 'angular', label: 'Angular', color: 'bg-red-500' },
{ id: 'solid', label: 'Solid', color: 'bg-blue-500' },
{ id: 'qwik', label: 'Qwik', color: 'bg-violet-500' },
]
const group = createGroup()
const tickets = group.onboard(tags.map(t => ({ id: t.id, value: t })))
const checkboxIcon = computed(() => {
if (group.isAllSelected.value) return 'i-lucide-check-square'
if (group.isMixed.value) return 'i-lucide-minus-square'
return 'i-lucide-square'
})
const results = computed(() => {
if (group.isNoneSelected.value) return tags
return tags.filter(t => group.selected(t.id))
})
</script>
<template>
<div class="space-y-6">
<!-- Header with tri-state checkbox -->
<div class="flex items-center justify-between">
<button
class="flex items-center gap-2 text-sm font-medium text-on-surface-variant hover:text-on-surface transition-colors"
@click="group.toggleAll()"
>
<div
class="size-5 transition-transform"
:class="checkboxIcon"
:style="{ transform: group.isMixed.value ? 'rotate(-90deg)' : 'rotate(0deg)' }"
/>
<span>{{ group.isAllSelected.value ? 'Clear all' : 'Select all' }}</span>
</button>
<span class="text-xs text-on-surface-variant">
{{ group.selectedIds.size }} / {{ group.size }} selected
</span>
</div>
<!-- Chip cloud -->
<div class="flex flex-wrap gap-2">
<button
v-for="ticket in tickets"
:key="ticket.id"
class="inline-flex items-center gap-1.5 px-3 py-1.5 rounded-full text-sm font-medium border transition-all duration-200"
:class="[
ticket.isSelected.value
? 'bg-surface text-on-surface border-primary scale-105'
: 'bg-surface text-on-surface-variant border-divider hover:border-primary/50'
]"
@click="ticket.toggle()"
>
<span
class="size-2 rounded-full transition-opacity duration-200"
:class="[ticket.value.color, ticket.isSelected.value ? 'opacity-100' : 'opacity-50']"
/>
{{ ticket.value.label }}
<span
class="i-lucide-check size-3.5 transition-all duration-200"
:class="ticket.isSelected.value ? 'opacity-100 scale-100' : 'opacity-0 scale-0'"
/>
</button>
</div>
<!-- Results preview -->
<div class="pt-4 border-t border-divider">
<p class="text-xs text-on-surface-variant mb-2">
{{ group.isNoneSelected.value ? 'Showing all' : 'Filtered results' }}:
</p>
<div class="flex flex-wrap gap-1">
<span
v-for="tag in results"
:key="tag.id"
class="px-2 py-0.5 text-xs rounded bg-surface-variant text-on-surface-variant"
>
{{ tag.label }}
</span>
</div>
</div>
</div>
</template>
Architecture
createGroup extends createSelection with multi-select and tri-state capabilities:
Reactivity
Group selection state is always reactive, including the tri-state mixedIds set.
| Property/Method | Reactive | Notes |
|---|---|---|
selectedIds | shallowReactive(Set) — always reactive | |
mixedIds | shallowReactive(Set) — tracks indeterminate state | |
selectedIndexes | Computed from selectedIds | |
isAllSelected | Computed from selectedIds and collection size | |
selectedItems | Computed from selectedIds | |
selectedValues | Computed from selectedItems | |
ticket isSelected | Computed from selectedIds |
Tri-state support mixedIds is reactive and updates automatically for indeterminate checkbox states in tree structures.
Functions
createGroup
(_options?: GroupOptions) => RCreates a new group instance with batch selection and tri-state support. Extends `createSelection` to support selecting, unselecting, and toggling multiple items at once by passing an array of IDs. Adds tri-state (mixed/indeterminate) support for checkbox trees and similar use cases.
Options
enroll
MaybeRefOrGetter<boolean>When true, newly registered items are automatically selected if not disabled. Useful for pre-selecting items in multi-select scenarios.
mandatory
MaybeRefOrGetter<boolean | "force">Controls mandatory selection behavior: - `false` (default): No mandatory selection enforcement - `true`: Prevents deselecting the last selected item (user must always have one selected) - `'force'`: Automatically selects the first non-disabled item on registration
Properties
Methods
seek
(direction?: "first" | "last", from?: number, predicate?: (ticket) => boolean) => Z | undefinedSeek for a ticket based on direction and optional predicate
on
<K extends Extensible<RegistryEventName>>(event: K, cb: EventHandler<Z, K>) => voidListen for registry events
off
<K extends Extensible<RegistryEventName>>(event: K, cb: EventHandler<Z, K>) => voidStop listening for registry events
emit
<K extends Extensible<RegistryEventName>>(event: K, data: EventPayload<Z, K>) => voidEmit an event with data
batch
<R>(fn: () => R) => RExecute operations in a batch, deferring cache invalidation and event emission until complete