Utilities
Standalone helpers for common UI patterns. These composables don’t depend on context or plugins—use them anywhere.
Overview
| Utility | Purpose |
|---|---|
| createFilter | Filter arrays with search queries |
| createPagination | Page navigation state |
| useVirtual | Virtual scrolling for large lists |
| createOverflow | Compute visible item capacity |
Tip
These utilities are standalone—they don’t require plugins or context. Use them anywhere, including outside Vue components.
createFilter
Filter arrays based on search queries:
ts
import { createFilter } from '@vuetify/v0'
const items = ref(['Apple', 'Banana', 'Cherry'])
const query = ref('')
const filter = createFilter()
const { items: filtered } = filter.apply(query, items)
query.value = 'an'
filtered.value // ['Banana']With Object Keys
ts
const users = ref([
{ name: 'Alice', email: 'alice@example.com' },
{ name: 'Bob', email: 'bob@example.com' },
])
const filter = createFilter({
keys: ['name', 'email'],
})
const query = ref('alice')
const { items: filtered } = filter.apply(query, users)
filtered.value // [{ name: 'Alice', ... }]Filter Modes
ts
const filter = createFilter({
mode: 'intersection', // 'some' | 'every' | 'union' | 'intersection'
keys: ['name', 'tags'],
})createPagination
Pagination state management:
ts
import { createPagination } from '@vuetify/v0'
const pagination = createPagination({
size: 100,
itemsPerPage: 10,
})
pagination.page.value // 1
pagination.pages // 10
pagination.isFirst.value // true
pagination.isLast.value // false
pagination.next() // Go to page 2
pagination.prev() // Go to page 1
pagination.select(5) // Go to page 5
pagination.first() // Go to page 1
pagination.last() // Go to page 10With Reactive Size
ts
const items = ref([...])
const pagination = createPagination({
size: () => items.value.length,
itemsPerPage: 20,
})Page Items
ts
pagination.items.value // [{ type: 'page', value: 1 }, { type: 'page', value: 2 }, ...]useVirtual
Virtual scrolling for large datasets:
ts
import { useVirtual } from '@vuetify/v0'
const items = ref(Array.from({ length: 10000 }, (_, i) => `Item ${i}`))
// useVirtual takes items as first arg, options as second
const virtual = useVirtual(items, {
itemHeight: 40,
})VirtualList.vue
<template>
<div ref="virtual.element" style="height: 400px; overflow: auto;">
<div :style="{ height: `${virtual.size}px`, paddingTop: `${virtual.offset}px` }">
<div
v-for="item in virtual.items"
:key="item.index"
style="height: 40px"
>
{{ item.raw }}
</div>
</div>
</div>
</template>Variable Height
ts
const virtual = useVirtual(items, {
height: 400, // Container height (or use element ref)
})createOverflow
Compute how many items fit in a container:
ts
import { createOverflow } from '@vuetify/v0'
const container = ref<HTMLElement>()
const overflow = createOverflow({
container,
itemWidth: 100,
gap: 8,
})
overflow.capacity.value // Number of items that fit
overflow.isOverflowing.value // Boolean: items exceed capacityUse Case: Responsive Chips
ResponsiveChips.vue
<template>
<div ref="container" class="flex gap-2">
<span v-for="tag in visibleTags" :key="tag" class="chip">
{{ tag }}
</span>
<span v-if="overflow.isOverflowing.value" class="chip">
+{{ tags.length - overflow.capacity.value }}
</span>
</div>
</template>
<script setup>
const tags = ['Vue', 'React', 'Angular', 'Svelte', 'Solid']
const visibleTags = computed(() => tags.slice(0, overflow.capacity.value))
</script>Transformers
Value transformation utilities:
toArray
Normalize any value to an array:
ts
import { toArray } from '@vuetify/v0'
toArray('single') // ['single']
toArray(['array']) // ['array']
toArray(null) // []
toArray(undefined) // []toReactive
Convert ref objects to reactive proxies:
ts
import { toReactive } from '@vuetify/v0'
const configRef = ref({ debug: false })
// Unwraps the ref and returns a reactive object
const config = toReactive(configRef)
config.debug // Reactive accessBest Practices
Combine Utilities
ts
// Filter + Paginate
const query = ref('')
const filter = createFilter()
const { items: filtered } = filter.apply(query, items)
const pagination = createPagination({
size: () => filtered.value.length,
itemsPerPage: 10,
})
const displayedItems = computed(() => {
const start = pagination.pageStart.value
const end = pagination.pageStop.value
return filtered.value.slice(start, end)
})Virtual + Filter
ts
const query = ref('')
const filter = createFilter()
const { items: filtered } = filter.apply(query, items)
const virtual = useVirtual(filtered, {
itemHeight: 40,
})Filter + Pagination + Virtual
For large datasets that need all three utilities working together:
ts
import { shallowRef, computed } from 'vue'
import { createFilter, createPagination, useVirtual } from '@vuetify/v0'
// Source data
const items = shallowRef(Array.from({ length: 10000 }, (_, i) => ({
id: i,
name: `Item ${i}`,
})))
// 1. Filter first
const query = shallowRef('')
const filter = createFilter({ keys: ['name'] })
const { items: filtered } = filter.apply(query, items)
// 2. Paginate the filtered results
const pagination = createPagination({
size: () => filtered.value.length,
itemsPerPage: 100,
})
// 3. Get current page slice
const pageItems = computed(() => {
const start = pagination.pageStart.value
const end = pagination.pageStop.value
return filtered.value.slice(start, end)
})
// 4. Virtual scroll the current page
const virtual = useVirtual(pageItems, { itemHeight: 40 })vue
<template>
<input v-model="query" placeholder="Search..." />
<div ref="virtual.element" style="height: 400px; overflow: auto;">
<div :style="{ height: `${virtual.size}px`, paddingTop: `${virtual.offset}px` }">
<div v-for="item in virtual.items" :key="item.raw.id" style="height: 40px">
{{ item.raw.name }}
</div>
</div>
</div>
<Pagination :pagination="pagination" />
</template>Each layer is reactive—changing the search query refilters, which updates pagination, which updates the virtual list.
