The Vite Search Plugin turns Markdown docs into search records during the Vite build. It is designed for md-plugins and Q-Press documentation sites that need search data without locking the project into one search provider.
The plugin produces a normalized search index first, then lets adapters decide how that data should be emitted. A small docs site can serve the generated JSON directly from Netlify or another static host. A larger docs site can use the same records as an upload payload for Meilisearch, Algolia, or a custom search service.
Why Search Output Is Build-Time
Docs content is usually known at build time. Generating the index during the build gives the search UI stable data that matches the deployed pages.
The plugin does not require a runtime server. The default JSON adapter works on static hosting by fetching the generated asset from the browser. Hosted services still need their own runtime search API, but the md-plugins build can produce the upload-ready data.
Use the Search UI page when you want the default front-end component that reads the generated JSON index.
Key Features
- Markdown discovery: Scan one or more Markdown roots with Q-Press-style route conventions.
- Static-host friendly: Emit
search/search-index.jsonfor client-side search without a server. - Adapter output: Emit JSON, Meilisearch-ready records, Algolia-ready records, or custom assets.
- Virtual module: Import the generated index from
virtual:md-plugins/search-index. - Frontmatter controls: Use fields such as
title,desc,tags,keys, andsearch: false. - Provider-neutral: Keep service upload keys out of the browser and outside the core plugin.
- UI-ready: Pair with
@md-plugins/search-uifor a framework-agnostic search component.
Installation
pnpm add -D @md-plugins/vite-search-pluginBasic Vite Setup
import { defineConfig } from 'vite'
import { viteSearchPlugin } from '@md-plugins/vite-search-plugin'
export default defineConfig({
plugins: [
viteSearchPlugin({
markdown: {
root: './src/markdown',
},
}),
],
})By default, the build emits:
dist/
search/
search-index.jsonThat file contains a generated timestamp, a record count, and the normalized records:
interface SearchIndex {
generatedAt: string
recordCount: number
records: SearchRecord[]
}Static Search
The default JSON adapter is the simplest path for Netlify and other static hosts. A search component can fetch the generated asset and run a lightweight client-side search over the records.
const response = await fetch('/search/search-index.json')
const searchIndex = await response.json()
const results = searchIndex.records.filter((record) =>
`${record.title} ${record.section ?? ''} ${record.content}`
.toLowerCase()
.includes(query.toLowerCase()),
)This is enough for smaller docs sites and does not require Meilisearch, Algolia, a server function, or build-time secrets.
Hosted Search Output
When a project wants hosted search, add adapters for the target provider:
import {
createAlgoliaAdapter,
createMeilisearchAdapter,
viteSearchPlugin,
} from '@md-plugins/vite-search-plugin'
export default defineConfig({
plugins: [
viteSearchPlugin({
markdown: {
root: './src/markdown',
},
adapters: [
'json',
createMeilisearchAdapter({
indexUid: 'docs',
fileName: 'search/meilisearch.json',
}),
createAlgoliaAdapter({
fileName: 'search/algolia.json',
}),
],
}),
],
})The hosted adapters only emit data. They do not upload directly. That separation is intentional: search service admin keys belong in CI or deploy scripts, not in a client-side Vite bundle.
Q-Press Routes
Markdown files use the same route convention as Q-Press SSG:
landing-page.mdbecomes/guide/getting-started.mdbecomes/guide/getting-startedguide/guide.mdbecomes/guide
Use routeBase when Markdown pages should be indexed under a route prefix:
viteSearchPlugin({
markdown: {
root: './src/markdown',
routeBase: '/docs',
},
})Frontmatter
The plugin reads common docs frontmatter by default:
---
title: Installation
desc: Install and configure the package.
tags:
- install
- vite
---Set search: false to exclude a page:
---
title: Private Draft
search: false
---The default field behavior is:
title: page titledescordescription: page summarytags,keys, orkeywords: searchable tagsbadgeandoverline: copied metadatasearch: false: skip the page
Virtual Module
Application code can import the generated index:
import searchIndex, { searchRecords } from 'virtual:md-plugins/search-index'The virtual module is useful when a project wants the search data bundled with application code. For larger sites, prefer fetching the emitted JSON asset so the search payload can be cached separately from the app bundle.
When To Use Each Output
- Use
jsonwhen the site is small or medium-sized and deployed to a static host. - Use
meilisearchwhen the project already has Meilisearch Cloud or a self-hosted Meilisearch instance. - Use
algoliawhen the project wants Algolia or DocSearch-style hosted search. - Use a custom adapter when a project needs Pagefind, a serverless search function, or a private search pipeline.