Composables
useSearchQuery
Manage search query state with URL sync and debouncing for responsive search experiences.
The useSearchQuery composable provides URL-synced search query state management with built-in debouncing. It automatically handles search input changes through the q query parameter, making search states shareable and bookmarkable while preventing excessive API calls during user typing.
Basic Usage
<script setup lang="ts">
const { q, qDebounced } = useSearchQuery()
// Use debounced value for API calls
const { data } = await useFetch('/api/search', {
query: {
q: qDebounced,
},
})
</script>
<template>
<div>
<!-- Input updates immediately -->
<UInput
v-model="q"
placeholder="Search..."
icon="i-heroicons-magnifying-glass"
/>
<!-- Results update after debounce -->
<SearchResults :items="data" />
</div>
</template>
Options
The useSearchQuery composable accepts an optional configuration object:
| Property | Type | Default | Description |
|---|---|---|---|
debounceDuration | number | 300 | Debounce delay in milliseconds before qDebounced updates |
route | ReturnType<typeof useRoute> | useRoute() | The route object to read query parameters from |
Return Value
Returns an object with two reactive properties:
q- Immediate search query (getter/setter that updates URL)qDebounced- Debounced search query (read-only, updates after delay)
Both properties automatically:
- Read from URL query parameter (
?q=search+term) - Update URL when
qchanges (vianavigateTo) - Remove empty values from URL for cleaner links
Examples
With Custom Debounce Duration
// Faster response for simpler searches
const { q, qDebounced } = useSearchQuery({ debounceDuration: 150 })
// Slower for expensive operations
const { q, qDebounced } = useSearchQuery({ debounceDuration: 500 })
Combined with Filters
composables/useProductSearch.ts
<script setup lang="ts">
const { q, qDebounced } = useSearchQuery()
const { page, pageSize } = usePagination()
const { data } = await useFetch('/api/products', {
query: {
q: qDebounced,
page,
size: pageSize,
},
})
</script>
<template>
<div>
<UInput v-model="q" placeholder="Search products..." />
<ProductList :products="data.items" />
<UPagination
v-model="page"
:total="data.total"
:page-size="pageSize"
/>
</div>
</template>
Accessing Immediate Value
Use q for real-time UI updates and qDebounced for API calls:
const { q, qDebounced } = useSearchQuery()
// Show what user is typing immediately
watch(q, (value) => {
console.log('User typed:', value)
})
// API calls only after user stops typing
watch(qDebounced, (value) => {
console.log('Searching for:', value)
})

