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:

PropertyTypeDefaultDescription
debounceDurationnumber300Debounce delay in milliseconds before qDebounced updates
routeReturnType<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 q changes (via navigateTo)
  • 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)
})

Copyright © 2026