Composables
Type-safe composables for headless UI. Components wrap these composables—you can use either approach.
Minimal Reactivity
Vuetify0 composables use the absolute minimum reactivity required to fulfill their responsibilities. This deliberate constraint keeps bundle sizes small, maximizes performance, and gives you full control over what triggers updates.
See Benchmarks for performance data and what the size ratings mean.
What’s Reactive
| Category | Reactive | Why |
|---|---|---|
Selection state (selectedIds, selectedValues) | UI must reflect selection changes | |
Ticket isSelected, isMixed | Per-item binding in templates | |
Plugin state (useTheme, useLocale, etc.) | App-wide singletons shared across components | |
Registry collections (values(), keys()) | Opt in via reactive: true or useProxyRegistry | |
| Ticket fields (custom props) | Opt in via reactive: true | |
| Registry size | Computed on access | |
| Queue / Timeline data | Opt in via reactive: true; queue also auto-fires events |
The reactive Option
For direct Vue reactivity on the collection and tickets themselves, pass reactive: true to the registry:
<script setup lang="ts">
import { createRegistry } from '@vuetify/v0'
const registry = createRegistry({ reactive: true })
const input = defineModel({ default: '' })
function onAdd () {
if (!input.value.trim()) return
registry.register({ value: input.value.trim() })
input.value = ''
}
</script>
<template>
<div class="flex gap-2 mb-3">
<input
v-model="input"
class="border border-divider rounded px-2 py-1 text-sm bg-surface text-on-surface flex-1"
placeholder="Add item..."
@keydown.enter="onAdd"
/>
<button class="px-3 py-1 text-sm rounded bg-primary text-on-primary" @click="onAdd">Add</button>
</div>
<ul class="space-y-1">
<li
v-for="ticket in registry.values()"
:key="ticket.id"
class="flex items-center justify-between text-sm px-2 py-1 rounded border border-divider"
>
{{ ticket.value }}
<button class="text-xs opacity-50 hover:opacity-100" @click="registry.unregister(ticket.id)">✕</button>
</li>
</ul>
<p class="mt-2 text-xs opacity-50">{{ registry.size }} item{{ registry.size === 1 ? '' : 's' }}</p>
</template> Don’t pass a registry directly to v-for. Pair events: true with useProxyRegistry() at the component boundary — the proxy exposes reactive keys, values, entries, and size properties that templates can iterate safely. Reading .values() from a reactive: true registry in a v-for breaks Vue’s dep tracking because the internal cache invalidates on every mutation.
Unlike events (which fire callbacks) or useProxyRegistry (which wraps with computed refs), reactive: true makes the underlying Map and each ticket shallowReactive. Use it when you need Vue’s dependency tracking at the ticket level.
useProxyModel for v-model Binding
When you need to sync a selection context with a v-model ref, use useProxyModel:
<script setup lang="ts">
import { createSelection, useProxyModel } from '@vuetify/v0'
import { toRef } from 'vue'
const model = defineModel({ default: 'blue' })
const colors = [
{ id: 'red', value: 'red', hex: '#ef4444' },
{ id: 'blue', value: 'blue', hex: '#3b82f6' },
{ id: 'green', value: 'green', hex: '#22c55e' },
]
const selection = createSelection({ events: true })
selection.onboard(colors)
useProxyModel(selection, model)
const active = toRef(() => colors.find(c => c.value === model.value))
</script>
<template>
<div class="flex gap-2">
<button
v-for="ticket in selection.values()"
:key="ticket.id"
class="size-8 rounded-full border-2 transition-all"
:class="ticket.isSelected.value ? 'border-on-surface scale-110' : 'border-transparent'"
:style="{ backgroundColor: colors.find(c => c.id === ticket.id)?.hex }"
@click="ticket.select()"
/>
</div>
<p class="mt-2 text-sm">Selected: {{ active?.id ?? '—' }}</p>
</template>Clicking a swatch calls ticket.select(), which updates model. Setting model.value = 'red' drives the selection back. Both directions stay in sync automatically.
useProxyRegistry for Full Reactivity
When you need automatic template updates for registry data, wrap it with useProxyRegistry:
import { createRegistry, useProxyRegistry } from '@vuetify/v0'
const registry = createRegistry()
// Non-reactive: template won't update when items change
const items = registry.values()
// Reactive: changes trigger template updates automatically
const proxy = useProxyRegistry(registry)
// proxy.values is a computed ref that updates on any mutationThis pattern lets you choose reactivity granularity—pay for what you use
Events as Opt-In Reactivity
Registries emit events when operations occur. Use events to build reactive behavior only where needed:
import { createRegistry } from '@vuetify/v0'
const registry = createRegistry({ events: true })
// Listen for changes
registry.on('register:ticket', ticket => {
console.log('Added:', ticket.id)
})
registry.on('unregister:ticket', ticket => {
console.log('Removed:', ticket.id)
})Available events:
register:ticket— Item addedunregister:ticket— Item removedupdate:ticket— Item modified viaupsert()clear:registry— All items removedreindex:registry— Indices recalculated
Naming Conventions
Composable names signal how they’re used:
create* — Factory Functions
Factory functions construct a new instance of stateful logic. They return an object you can provide, pass around, or destructure.
| Pattern | Example | When to Use |
|---|---|---|
| Context factories | createContext, createTrinity | Building reusable provide/inject pairs |
| State factories | createSelection, createRegistry | Creating isolated state instances |
| Feature factories | createDataTable, createForm | Composing multiple primitives into a feature |
use* — Composables
Composables consume existing context or wrap browser APIs. They’re called inside setup() and return reactive state.
| Pattern | Example | When to Use |
|---|---|---|
| Plugin consumers | useTheme, useLocale, useStorage | Reading app-level plugin state (requires plugin installation) |
| Browser wrappers | useEventListener, useResizeObserver | Safe, lifecycle-managed browser API access |
| Behavior composables | useHotkey, useClickOutside | Adding interactive behavior to elements |
Foundation
Core factories that provide the foundation for all other composables.
| Name | Description |
|---|---|
| createContext | Create reusable context to share state across components |
| createPlugin | Create Vue plugins with standardized patterns |
| createTrinity | Create context provider/consumer pattern utilities |
Registration
Collection management and data structure primitives.
| Name | Description |
|---|---|
| createRegistry | Foundation for registration-based systems |
| createQueue | Time-based queue management with automatic timeouts |
| createTimeline | Bounded undo/redo system with fixed-size history |
| createTokens | Design token management system |
Selection
State management for single and multi-selection patterns.
| Name | Description |
|---|---|
| createModel | Value store layer with disabled guards and apply bridge |
| createNested | Hierarchical parent-child relationships with open/close state and tree traversal |
| createSelection | General selection state management |
| createSingle | Single-selection with automatic deselection |
| createGroup | Multi-selection with tri-state support |
| createStep | Sequential navigation for wizards and steppers |
Forms
Form state management and model binding utilities.
| Name | Description |
|---|---|
| createCombobox | Coordinate selection, popover, virtual focus, and filtering for autocomplete |
| createForm | Form validation coordinator |
| createInput | Shared form field primitive: validation, field state, ARIA IDs |
| createNumberField | Numeric input state with formatting, parsing, and validation |
| createRating | Bounded rating value with discrete items and half-step support |
| createSlider | Slider state with multi-thumb support, step snapping, and value math |
| createValidation | Per-field validation lifecycle |
Reactivity
Reactive proxy utilities for bridging state.
| Name | Description |
|---|---|
| useProxyModel | Bridge selection context to v-model binding |
| useProxyRegistry | Proxy-based registry with automatic reactivity |
System
Browser API wrappers with automatic lifecycle cleanup.
| Name | Description |
|---|---|
| useClickOutside | Detect clicks outside an element |
| useEventListener | Handle DOM events with automatic cleanup |
| useHotkey | Handle hotkey combinations and sequences |
| useImage | Image loading state machine with deferred loading and retry |
| useIntersectionObserver | Intersection Observer API for visibility detection |
| useLazy | Defer rendering until first activation for dialogs, menus, and tooltips |
| useMediaQuery | Reactive CSS media query matching |
| useMutationObserver | Mutation Observer API for DOM change detection |
| usePopover | Native Popover API behavior with CSS anchor positioning |
| usePresence | Animation-agnostic mount lifecycle with lazy mounting and exit timing |
| useRaf | Scope-safe requestAnimationFrame with automatic cleanup |
| useResizeObserver | Resize Observer API for element size changes |
| useRovingFocus | Roving tabindex keyboard navigation for composite widgets |
| useTimer | Reactive timer with pause/resume controls and remaining time tracking |
| useToggleScope | Conditional effect scope management |
| useVirtualFocus | Virtual focus for aria-activedescendant keyboard navigation patterns |
Plugins
Application-level features installable via Vue plugins.
| Name | Description |
|---|---|
| useBreakpoints | Responsive breakpoint detection |
| useDate | Date manipulation with Temporal API and locale-aware formatting |
| useFeatures | Feature flags and A/B testing |
| useHydration | SSR hydration management |
| useLocale | Internationalization system |
| useLogger | Logging system with multiple adapters |
| useNotifications | Notification lifecycle with toast queue and service adapter integration |
| usePermissions | Role-based access control |
| useRtl | Reactive RTL text direction with dir attribute management |
| useRules | Validation rule aliases with locale-aware messages |
| useStack | Overlay z-index stacking with automatic calculation and scrim integration |
| useStorage | Reactive browser storage interface |
| useTheme | Theme management with CSS custom properties |
Data
Composables for filtering, sorting, paginating, and virtualizing collections.
| Name | Description |
|---|---|
| createDataTable | Composable data table with sort, filter, paginate, select, and expand |
| createFilter | Filter arrays based on search queries |
| createPagination | Pagination state with navigation methods |
| createVirtual | Virtual scrolling for large lists |
Semantic
Composables for presentational and semantic components.
| Name | Description |
|---|---|
| createBreadcrumbs | Breadcrumb navigation with path truncation |
| createOverflow | Compute item capacity for responsive truncation |
| createProgress | Progress tracking with multi-segment registration |
Transformers
Value transformation utilities.
| Name | Description |
|---|---|
| toArray | Convert any value to an array |
| toElement | Resolve refs, getters, or component instances to a plain DOM element |
| toReactive | Convert MaybeRef objects to reactive proxies |