
A composable for filtering arrays of items based on search queries, supporting both primitive values and complex objects with customizable filtering logic.
The useFilter composable provides reactive array filtering with multiple modes for different search behaviors. It works with both primitive values and complex objects, and supports filtering by specific keys.
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
const items = ref([
{ name: 'John Doe', age: 30, city: 'New York' },
{ name: 'Jane Doe', age: 25, city: 'Los Angeles' },
{ name: 'Peter Jones', age: 40, city: 'Chicago' },
])
const query = ref('doe')
const { items: filtered } = useFilter(query, items, { keys: ['name'] })
console.log(filtered.value)
// [
// { name: 'John Doe', age: 30, city: 'New York' },
// { name: 'Jane Doe', age: 25, city: 'Los Angeles' }
// ]| Composable | Description |
|---|---|
| useSelection→ | Selection system for collections |
| useRegistry→ | Base registry system for managing items |
| useProxyModel→ | Two-way binding for filtered selections |
Type
export type Primitive = string | number | boolean
export type FilterQuery = MaybeRefOrGetter<Primitive | Primitive[]>
export type FilterItem = Primitive | Record<string, any>
export type FilterMode = 'some' | 'every' | 'union' | 'intersection'
export type FilterFunction = (query: Primitive | Primitive[], item: FilterItem) => boolean
export interface UseFilterOptions {
customFilter?: FilterFunction
keys?: string[]
mode?: FilterMode
}
export interface UseFilterResult<Z extends FilterItem = FilterItem> {
items: ComputedRef<Z[]>
}
function useFilter<Z extends FilterItem>(
query: FilterQuery,
items: MaybeRef<Z[]>,
options?: UseFilterOptions
): UseFilterResult<Z>Details
The useFilter composable accepts a query (string, number, boolean, or array), an array of items to filter, and optional configuration. It returns a reactive items computed property containing the filtered results.
Filter Modes:
some (default): Match if ANY field contains the queryevery: Match if ALL fields contain the queryunion: Match if ANY query matches ANY field (OR logic for multiple queries)intersection: Match if ALL queries match at least one field (AND logic for multiple queries)Parameters
query: The search query (reactive). Can be a single value or array of values.items: The array to filter (reactive or static).options.keys: Array of object keys to filter by. If omitted, all values are searched.options.mode: Filter mode ('some', 'every', 'union', 'intersection').options.customFilter: Custom filter function for advanced use cases.Returns
items: Computed property containing filtered items.Filter an array of objects by a search term:
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
const users = ref([
{ id: 1, name: 'Alice Smith', role: 'Admin' },
{ id: 2, name: 'Bob Jones', role: 'User' },
{ id: 3, name: 'Charlie Smith', role: 'User' },
])
const searchQuery = ref('smith')
const { items: filteredUsers } = useFilter(searchQuery, users, {
keys: ['name']
})
console.log(filteredUsers.value)
// [
// { id: 1, name: 'Alice Smith', role: 'Admin' },
// { id: 3, name: 'Charlie Smith', role: 'User' }
// ]Filter an array of primitive values:
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
const fruits = ref(['apple', 'banana', 'cherry', 'apricot', 'blueberry'])
const query = ref('ap')
const { items: filtered } = useFilter(query, fruits)
console.log(filtered.value)
// ['apple', 'apricot']Match items where ANY field contains the query:
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
const products = ref([
{ name: 'Laptop', category: 'Electronics', price: 999 },
{ name: 'Phone', category: 'Electronics', price: 599 },
{ name: 'Desk', category: 'Furniture', price: 299 },
])
const query = ref('electronics')
const { items: filtered } = useFilter(query, products, { mode: 'some' })
console.log(filtered.value)
// [
// { name: 'Laptop', category: 'Electronics', price: 999 },
// { name: 'Phone', category: 'Electronics', price: 599 }
// ]Match items where ALL fields contain the query:
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
const items = ref([
{ title: 'Vue Guide', tags: 'vue, guide, tutorial' },
{ title: 'React Guide', tags: 'react, guide' },
{ title: 'Vue API', tags: 'vue, api, reference' },
])
const query = ref('vue')
const { items: filtered } = useFilter(query, items, { mode: 'every' })
console.log(filtered.value)
// [
// { title: 'Vue Guide', tags: 'vue, guide, tutorial' },
// { title: 'Vue API', tags: 'vue, api, reference' }
// ]Match items where ANY query matches ANY field:
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
const books = ref([
{ title: 'JavaScript Basics', author: 'John Doe' },
{ title: 'Vue.js Guide', author: 'Jane Smith' },
{ title: 'TypeScript Deep Dive', author: 'John Smith' },
])
// Search for items containing 'vue' OR 'john'
const queries = ref(['vue', 'john'])
const { items: filtered } = useFilter(queries, books, { mode: 'union' })
console.log(filtered.value)
// [
// { title: 'JavaScript Basics', author: 'John Doe' },
// { title: 'Vue.js Guide', author: 'Jane Smith' },
// { title: 'TypeScript Deep Dive', author: 'John Smith' }
// ]Match items where ALL queries match at least one field:
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
const articles = ref([
{ title: 'Vue 3 Composition API', tags: 'vue, composition, api' },
{ title: 'Vue 2 Options API', tags: 'vue, options' },
{ title: 'React Hooks Guide', tags: 'react, hooks, api' },
])
// Search for items containing BOTH 'vue' AND 'api'
const queries = ref(['vue', 'api'])
const { items: filtered } = useFilter(queries, articles, { mode: 'intersection' })
console.log(filtered.value)
// [
// { title: 'Vue 3 Composition API', tags: 'vue, composition, api' },
// { title: 'Vue 2 Options API', tags: 'vue, options' }
// ]Limit the search to specific object properties:
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
const employees = ref([
{ name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering' },
{ name: 'Bob Smith', email: 'bob@example.com', department: 'Marketing' },
{ name: 'Charlie Johnson', email: 'charlie@example.com', department: 'Sales' },
])
// Search only in the 'name' field
const query = ref('johnson')
const { items: filtered } = useFilter(query, employees, { keys: ['name'] })
console.log(filtered.value)
// [
// { name: 'Alice Johnson', email: 'alice@example.com', department: 'Engineering' },
// { name: 'Charlie Johnson', email: 'charlie@example.com', department: 'Sales' }
// ]Implement custom filtering logic for advanced use cases:
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
const products = ref([
{ name: 'Laptop', price: 999, inStock: true },
{ name: 'Phone', price: 599, inStock: false },
{ name: 'Tablet', price: 399, inStock: true },
])
// Custom filter: search by name and filter by price range
const priceRange = ref([300, 700])
const { items: filtered } = useFilter(priceRange, products, {
customFilter: (query, item) => {
if (typeof item === 'object' && item !== null) {
const [min, max] = query as number[]
return item.price >= min && item.price <= max && item.inStock
}
return false
}
})
console.log(filtered.value)
// [{ name: 'Tablet', price: 399, inStock: true }]When the query is empty or only whitespace, all items are returned:
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
const items = ref(['apple', 'banana', 'cherry'])
const query = ref('')
const { items: filtered } = useFilter(query, items)
console.log(filtered.value)
// ['apple', 'banana', 'cherry']
query.value = ' ' // Only whitespace
console.log(filtered.value)
// ['apple', 'banana', 'cherry']A practical example of a searchable user list:
<script setup lang="ts">
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
interface User {
id: number
name: string
email: string
department: string
}
const users = ref<User[]>([
{ id: 1, name: 'Alice Johnson', email: 'alice@company.com', department: 'Engineering' },
{ id: 2, name: 'Bob Smith', email: 'bob@company.com', department: 'Marketing' },
{ id: 3, name: 'Charlie Brown', email: 'charlie@company.com', department: 'Engineering' },
{ id: 4, name: 'Diana Prince', email: 'diana@company.com', department: 'Sales' },
])
const searchQuery = ref('')
const searchFields = ref<string[]>(['name', 'email', 'department'])
const { items: filteredUsers } = useFilter(searchQuery, users, {
keys: searchFields.value,
mode: 'some'
})
</script>
<template>
<div>
<input
v-model="searchQuery"
type="text"
placeholder="Search users..."
/>
<div v-if="filteredUsers.length === 0">
No users found matching "{{ searchQuery }}"
</div>
<ul v-else>
<li v-for="user in filteredUsers" :key="user.id">
<strong>{{ user.name }}</strong> - {{ user.email }} ({{ user.department }})
</li>
</ul>
</div>
</template>The composable is fully typed and supports generic type parameters:
import { ref } from 'vue'
import { useFilter } from '@vuetify/v0'
interface Product {
id: number
name: string
category: string
price: number
}
const products = ref<Product[]>([
{ id: 1, name: 'Laptop', category: 'Electronics', price: 999 },
{ id: 2, name: 'Desk', category: 'Furniture', price: 299 },
])
const query = ref('laptop')
const { items } = useFilter<Product>(query, products, {
keys: ['name', 'category']
})
// items is typed as ComputedRef<Product[]>
console.log(items.value[0]?.name) // TypeScript knows this is a string.includes())customFilter function receives the full query parameter (single value or array) and must handle it accordingly