Composables
Composables are the foundation of v0. They provide headless logic that you can use directly or through wrapper components.
Composables vs Components
Both approaches use the same underlying logic:
ts
// Direct composable usage
const selection = createSelection({ multiple: true })
selection.register({ id: 'a', value: 'Apple' })
selection.select('a')vue
<template>
<Selection.Root v-model="selected" multiple>
<Selection.Item value="Apple">Apple</Selection.Item>
</Selection.Root>
</template>When to Use Each
| Use Composables When | Use Components When |
|---|---|
| Need full control over rendering | Want declarative templates |
| Building custom abstractions | Standard UI patterns |
| Non-DOM contexts (stores, workers) | Accessibility attrs needed |
| Maximum flexibility | Faster development |
Tip
Components and composables are interchangeable. Every component uses a composable internally—you can always drop to the composable for more control.
Ask AI
How do I choose between composables and components for my use case?
Categories
Foundation
Factories that create other composables:
| Composable | Purpose |
|---|---|
| createContext | Type-safe provide/inject |
| createTrinity | [use, provide, context] tuple |
| createPlugin | Vue plugin factory |
Registration
Collection management primitives:
| Composable | Purpose |
|---|---|
| createRegistry | Base collection with lookup |
| createTokens | Design token aliases |
| createQueue | Time-based queue |
| createTimeline | Undo/redo history |
Selection
State management for selection patterns:
| Composable | Purpose |
|---|---|
| createSelection | Multi-select base |
| createSingle | Radio, tabs, accordion |
| createGroup | Checkboxes, tri-state |
| createStep | Wizard, stepper, carousel |
Forms
Form state and validation:
| Composable | Purpose |
|---|---|
| createForm | Validation, dirty tracking |
Reactivity
Reactive proxy utilities:
| Composable | Purpose |
|---|---|
| useProxyModel | v-model bridge |
| useProxyRegistry | Registry to reactive object |
Plugins
App-level features installed via app.use():
| Composable | Purpose |
|---|---|
| useTheme | Dark/light mode |
| useLocale | i18n, RTL |
| useBreakpoints | Responsive queries |
| useStorage | Persistent state |
Utilities
Standalone helpers:
| Composable | Purpose |
|---|---|
| useFilter | Array filtering |
| usePagination | Page navigation |
| useVirtual | Virtual scrolling |
Usage Patterns
Direct Factory Call
For standalone instances:
ts
import { createSelection } from '@vuetify/v0'
const tabs = createSelection({ multiple: false })
tabs.register({ id: 'home', value: 'Home' })
tabs.register({ id: 'about', value: 'About' })
tabs.select('home')Context Injection
For component tree sharing:
ts
// Parent
import { createSelectionContext } from '@vuetify/v0'
const [useTabSelection, provideTabSelection] = createSelectionContext({
namespace: 'tabs',
multiple: false,
})
provideTabSelection()
// Child
const selection = useTabSelection()
selection.select('home')Plugin Installation
For app-wide singletons:
main.ts
import { createApp } from 'vue'
import { createThemePlugin } from '@vuetify/v0'
const app = createApp(App)
app.use(
createThemePlugin({
default: 'light',
themes: { light: {...}, dark: {...} },
}),
)Composing Composables
Build complex behavior by combining primitives:
composables/useDataTable.ts
import { createSelection, createFilter, createPagination } from '@vuetify/v0'
// Filterable, paginated selection
const items = ref([...])
const query = ref('')
const filter = createFilter()
const { items: filtered } = filter.apply(query, items)
const pagination = createPagination({
size: () => filtered.value.length,
itemsPerPage: 10,
})
const selection = createSelection({ multiple: true })
// Visible items with selection state
const visibleItems = computed(() => {
const start = pagination.pageStart.value
const end = pagination.pageStop.value
return filtered.value.slice(start, end)
})TypeScript
All composables are fully typed. The value type is inferred from registration:
ts
interface MyItem {
id: string
label: string
}
const selection = createSelection()
selection.register({ id: '1', value: { id: '1', label: 'First' } as MyItem })
// Type-safe access via ticket
const ticket = selection.get('1')
ticket?.value // MyItemAsk AI
Which composables should I use for a data table with filtering and pagination?
