createRegistry
Foundation for ordered, keyed collections. Items are registered with IDs and indexes, and can be looked up by ID, index, or value.
Usage
The createRegistry composable provides a powerful interface for managing collections of items in a registration-based system. It allows you to register, unregister, and look up items efficiently, while maintaining an index for quick access.
import { createRegistry } from '@vuetify/v0'
import type { RegistryTicket } from '@vuetify/v0'
interface Item extends RegistryTicket<string> {
label: string
}
const registry = createRegistry<Item>()
// Register individual items
const a = registry.register({ label: 'Alpha' })
const b = registry.register({ label: 'Beta' })
const c = registry.register({ label: 'Gamma' })
console.log(registry.size) // 3
console.log(a.index) // 0
// Look up by id
const found = registry.get(b.id)
console.log(found?.label) // 'Beta'
// Patch a field without replacing the ticket
registry.upsert(b.id, { label: 'Beta (updated)' })
// Move to a new position
registry.move(a.id, 2)
console.log(a.index) // 2
// Remove one
registry.unregister(c.id)
console.log(registry.size) // 2
// Bulk load
registry.onboard([
{ id: 'x', label: 'X' },
{ id: 'y', label: 'Y' },
])
// Bulk remove
registry.offboard(['x', 'y'])Context / DI
Use createRegistryContext to share a registry across a component tree:
import { createRegistryContext } from '@vuetify/v0'
export const [useItems, provideItems, items] =
createRegistryContext({ namespace: 'my:items' })
// In parent component
provideItems()
// In child component
const registry = useItems()
registry.register({ id: 'item-1', value: 'First' })Architecture
createRegistry is the foundation for specialized registration systems:
Each branch extends the base ticket pattern with domain-specific capabilities. See individual composable docs for their extension hierarchies.
Reactivity
createRegistry uses minimal reactivity by default for performance. Collection methods are not reactive unless you opt in.
| Method | Notes |
|---|---|
register(ticket) | Add a ticket to the registry |
unregister(id) | Remove a ticket by ID |
upsert(id, partial) | Register or update a ticket |
move(id, index) | Reorder a ticket to a new index position |
onboard(tickets) | Batch-register an array of tickets |
offboard(ids) | Batch-unregister an array of IDs |
batch(fn) | Run multiple mutations with a single cache invalidation and deferred events |
get(id) | Retrieve a ticket by ID |
has(id) | Check whether a ticket ID is registered |
browse(value) | Reverse-lookup — find ticket ID(s) by value |
lookup(index) | Find ticket ID by zero-based index |
seek(direction, from?, predicate?) | Find 'first' or 'last' ticket, optionally starting from an index and/or filtered by predicate |
keys() | All registered IDs as a readonly array |
values() | All registered tickets as a readonly array |
entries() | All [id, ticket] pairs as a readonly array |
clear() | Remove all tickets |
dispose() | Remove all tickets and clear event listeners |
Need reactive collections? Use useProxyRegistry(registry) with events: true for reliable template reactivity. While reactive: true makes the internal collection a shallowReactive Map, values() caches its result — after any re-render not triggered by a collection mutation, Vue loses the reactive dependency on the collection, and future mutations won’t trigger re-renders. See useProxyRegistry for details.
Examples
Task Manager
This example demonstrates the full createRegistry lifecycle paired with createContext so registry mutations stay encapsulated in the provider and the consumer only sees clean, typed methods.
File breakdown:
| File | Role |
|---|---|
context.ts | Defines TaskTicketInput (extending RegistryTicketInput) and TaskContext, then creates the [useTaskRegistry, provideTaskRegistry] tuple |
TaskProvider.vue | Creates the registry with events: true, wires lifecycle listeners, seeds initial data via onboard, and exposes mutation methods through context |
TaskConsumer.vue | Calls useTaskRegistry() to access tasks and methods; owns local UI state (filter, new-task input) and derives filteredTasks and stats as computed |
task-manager.vue | Entry point—composes TaskProvider around TaskConsumer |
Key patterns:
onboard — bulk-loads the initial task list in a single batch
registry.register()— adds a ticket with custom fields (value,priority,done)registry.upsert()— patches a single field without touching the rest of the ticketregistry.move()— moves a ticket to a new index position, triggers reindexregistry.offboard()— batch-removes all completed tasks in one callregistry.on('register:ticket')/on('unregister:ticket')— reacts to lifecycle events for the audit logvoid version.valueinside a computed — the standard pattern for making a non-reactiveregistry.values()snapshot reactive
Add tasks, toggle completion, and filter by priority. Watch the event log at the bottom track every registration change in real time.
Event log
+ Registered "Set up CI pipeline"
+ Registered "Write unit tests"
+ Registered "Update README"
+ Registered "Review PR #42"
+ Registered "Refactor auth module"
Functions
createRegistry
(options?: RegistryOptions | undefined) => RegistryContext<Z, E>Creates a new registry instance.
createRegistryContext
(_options?: RegistryContextOptions) => ContextTrinity<RegistryContext<Z, E>>Creates a new registry context.
Options
Properties
Methods
move
(id: ID, toIndex: number) => E | undefinedSeek for a ticket based on direction and optional predicate
seek
(direction?: "first" | "last", from?: number, predicate?: (ticket) => boolean) => E | undefinedon
<K extends Extensible<RegistryEventName>>(event: K, cb: EventHandler<E, K>) => voidListen for registry events
off
<K extends Extensible<RegistryEventName>>(event: K, cb: EventHandler<E, K>) => voidStop listening for registry events
emit
<K extends Extensible<RegistryEventName>>(event: K, data: EventPayload<E, K>) => voidEmit an event with data
batch
<R>(fn: () => R) => RExecute operations in a batch, deferring cache invalidation and event emission until complete
Benchmarks
Every operation is profiled across multiple dataset sizes to measure real-world throughput. Each benchmark is assigned a performance tier—good, fast, blazing, or slow—and groups are scored by averaging their individual results so you can spot bottlenecks at a glance. This transparency helps you make informed decisions about which patterns scale for your use case. Learn more in the benchmarks guide.