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' or 'content').
      • minTermLength: Skip terms shorter than this value. Default is 1.
      • weights: Control ranking behavior.
        • title: Boost factor for title matches. Default is 10.
        • content: Boost factor for content matches. Default is 5.
        • heading: Whether higher-level sections (h1 > h2 > h3) rank higher. Default is true.
      • snippet: Return highlighted text excerpts.
        • columns: Which columns to snippet (['title'], ['content'], or both). 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
  snippets?: { title?: string, content?: 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: { columns: ['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