Query Utils

useSearchCollection

The useSearchCollection composable provides full-text search powered by SQLite FTS5, with prefix matching, BM25 ranking, and snippets.

Usage

Use the auto-imported useSearchCollection composable to search across one or more collections. It builds an FTS5 index from content sections and provides instant ranked search results.

app.vue
<script setup lang="ts">
const { status, search } = useSearchCollection('docs')

const query = ref('')
const results = ref([])

watch(query, async (value) => {
  results.value = value ? await search(value) : []
})
</script>
useSearchCollection is client-only. The FTS5 index is built in the browser using SQLite WASM.

Type

function useSearchCollection<T extends keyof PageCollections>(
  collection: T | T[],
  opts?: GenerateSearchSectionsOptions & { immediate?: boolean }
): {
  status: Ref<'idle' | 'loading' | 'ready' | 'error'>
  search: (query: string, opts?: SearchCollectionOptions) => Promise<SearchResult[]>
  init: () => Promise<DatabaseAdapter>
}

API

Parameters

  • collection: A single collection key or an array of collection keys to search across.
  • opts: (Optional) Index-building options:
    • immediate: Whether to start building the index immediately. Default is true. Set to false to defer until the first search() call or explicit init().
    • ignoredTags: Tags to ignore when extracting section content (e.g., ['code']).
    • minHeading: Minimum heading level to split sections on (e.g., 'h2'). Default is 'h1'.
    • maxHeading: Maximum heading level to split sections on (e.g., 'h4'). Default is 'h6'.

Return Values

  • status: A reactive ref indicating the index state: 'idle', 'loading', 'ready', or 'error'.
  • search(query, opts?): Execute a search query. Returns a promise with ranked results.
    • query: The search string. Supports prefix matching automatically (typing compo matches "composable").
    • opts: (Optional) Search options:
      • limit: Maximum results. Default is 50.
      • fields: Restrict search to specific columns ('title', 'content', 'titles').
      • minMatchCharLength: Skip terms shorter than this value. Default is 1.
      • snippet: Return a highlighted text excerpt.
        • column: Which column to snippet ('title' or 'content'). Default is 'content'.
        • around: Number of tokens around the match. Default is 30.
        • tag: HTML tag for highlighting. Default is 'mark'.
  • init(): Manually trigger index building. Useful when immediate: false.

Result Type

interface SearchResult {
  collection: string
  id: string
  title: string
  titles: string[]
  level: number
  content: string
  rank: number
  snippet?: string
}

Examples

SearchPage.vue
<script setup lang="ts">
const { status, search } = useSearchCollection('docs')

const query = ref('')
const results = ref([])

async function onSearch() {
  results.value = query.value
    ? await search(query.value, { limit: 20 })
    : []
}
</script>

<template>
  <UInput v-model="query" :disabled="status !== 'ready'" @input="onSearch" />
  <ul>
    <li v-for="result in results" :key="result.id">
      <NuxtLink :to="result.id">{{ result.title }}</NuxtLink>
    </li>
  </ul>
</template>
GlobalSearch.vue
<script setup lang="ts">
const { status, search } = useSearchCollection(['docs', 'blog'])

const results = ref([])
const query = ref('')

watch(query, async (value) => {
  results.value = value
    ? await search(value, {
        limit: 20,
        snippet: { column: 'content', around: 40 },
      })
    : []
})
</script>

Deferred Initialization

LazySearch.vue
<script setup lang="ts">
const { status, search, init } = useSearchCollection('docs', {
  immediate: false,
})

async function onFocus() {
  if (status.value === 'idle') {
    await init()
  }
}
</script>

Compared to queryCollectionSearchSections

useSearchCollectionqueryCollectionSearchSections + Fuse.js
DependenciesNone (built-in FTS5)Requires external library
IndexSQLite inverted indexIn-memory JS scan
SpeedO(1) lookupO(n) per query
SnippetsBuilt-inManual
Typo tolerancePrefix onlyFull fuzzy (edit distance)
Multi-collectionNativeManual merging

Use useSearchCollection when you need fast, zero-dependency search. Use queryCollectionSearchSections with Fuse.js or MiniSearch when you need typo-tolerant fuzzy matching.

Copyright © 2026