Search
Client-side search that works out of the box with no API keys, plus optional hosted and semantic backends you can switch to as your docs grow.
Blume ships local search with no hosted infrastructure and no API keys. It runs in the browser, works in both blume dev and blume build, and indexes only your real content — navigation chrome and excluded pages are skipped. When you outgrow it, you can switch to a hosted or semantic backend without changing how search looks or behaves — only the search.provider you configure changes.
Blume reaches parity with Fumadocs’ provider set: Orama, FlexSearch, Algolia, Orama Cloud, Typesense, and Mixedbread (plus Pagefind). Only the configured provider’s SDK is installed into your project, so picking one backend never pulls in the others.
Using search
Open search with ⌘K (or Ctrl K), or press / when you’re not typing in a field. Esc closes it, and ⌘J (or Ctrl J) toggles the result preview pane.
Queries match page titles, descriptions, and body text, with title matches ranked highest and descriptions above body.
Popular pages
Before a reader types a query, the search dialog shows a Popular list. By default it is the first six sidebar pages — which on multi-tab sites often surfaces the wrong section. Pin the links you want instead:
search: {
popular: [
{ href: "/guides/getting-started", icon: "rocket", label: "Getting started" },
{ href: "/guides/install", icon: "download", label: "Install" },
{ href: "/concepts/overview", label: "Overview" },
],
},
Each entry takes an href (internal route or external URL) and a label, plus an optional icon — a built-in icon name shown beside the label, defaulting to a file glyph. Omit popular or leave it empty to keep the sidebar fallback.
Unlike icons elsewhere in Blume, icon here must be a built-in name: these rows render in a client island, so an image path or inline SVG isn’t supported and falls back to the file glyph.
Write href as if the site were mounted at the root — a basePath is applied for you, the same as navigation.featured. External URLs pass through untouched.
What’s indexed
For every indexable page, Blume indexes its title, description, and body reduced to plain text — code blocks, images, and markup are stripped, so results stay relevant. The index is built from your source files, so it’s identical in dev and production.
Tags
Add search.tags to a page’s frontmatter to group it under a filter in the search dialog — readers can narrow results to a tag with a click. Tags also become a facet on the hosted providers.
search:
tags: [api, reference]
Providers
The client-side providers are keyless and need no extra config. The hosted ones take public credentials in blume.config.ts (safe to ship to the browser) and read their secret admin key from an environment variable at build time — the secret never lands in the config or the client bundle.
Orama (default)
Blume’s default engine. It builds a JSON index served at /blume-search.json and queries it in the browser — instant, client-side, and live in blume dev as you edit. No keys, no service.
search: {
provider: "orama", // default
}
Languages written without spaces
Orama’s standard tokenizer splits on word boundaries that only exist in space-separated scripts, so Japanese, Chinese, Korean, and Thai text would otherwise produce no matches at all. Blume handles this for you: when i18n.defaultLocale is one of those languages, the index switches to a word-segmenting tokenizer (built on the browser- and Node-native Intl.Segmenter). Declaring your site’s language is all it takes:
i18n: {
defaultLocale: "ja",
locales: [{ code: "ja", label: "日本語" }],
}
The same tokenizer serves the search dialog, the MCP server’s search_docs tool, and Ask AI grounding. On a mixed-language site the whole index shares the default locale’s tokenizer — that’s safe, because Latin words survive segmentation intact, so pages in English (or any spaced language) stay searchable alongside the default language.
Japanese and Chinese go one step further. Segmenting alone indexes a compound term as its parts — 資金決済法 as 資金, 決済 and 法 — which lets a page mentioning each part somewhere outrank the page the term is actually about. Han, Hiragana and Katakana are therefore indexed as overlapping character pairs, and queries on those indexes prefer pages carrying a term’s pairs together, loosening to any-pair matching when no page carries them all, so typing a whole sentence still returns its closest pages. Korean and Thai keep their segmented words.
FlexSearch
A second keyless, client-side option. It reuses the same /blume-search.json index Orama ships and builds a FlexSearch document index in the browser. Works in blume dev and blume build.
FlexSearch has no equivalent segmentation hook, so for sites in Japanese, Chinese, Korean, or Thai prefer Orama (the default) or Pagefind, whose pagefind_extended binary segments those languages natively.
search: {
provider: "flexsearch",
}
Pagefind
For very large docs, opt into Pagefind. It indexes your built HTML and loads the index in shards on demand, keeping the initial payload tiny no matter how big the site grows.
search: {
provider: "pagefind",
}
Pagefind only runs during blume build, so search isn’t available in blume dev with this provider.
Algolia
The browser queries Algolia directly with your search-only key. Each blume build replaces the index using the admin key from ALGOLIA_ADMIN_API_KEY (the build warns and skips the upload if it’s unset). The whole index is replaced on every sync, so pages you delete or rename don’t linger as stale results.
search: {
provider: "algolia",
algolia: {
appId: "YOUR_APP_ID",
indexName: "docs",
searchApiKey: "YOUR_SEARCH_ONLY_KEY", // public
},
}
Orama Cloud
Hosted Orama. The browser queries your index endpoint with the public API key; blume build pushes records to the index using ORAMA_PRIVATE_API_KEY. Set indexId to enable the sync.
search: {
provider: "orama-cloud",
oramaCloud: {
endpoint: "https://cloud.orama.run/v1/indexes/your-index",
apiKey: "YOUR_PUBLIC_API_KEY",
indexId: "your-index-id", // for the build-time sync
},
}
Typesense
Self-hosted or cloud Typesense. The browser queries the collection with the search-only key; blume build recreates the collection and imports documents using TYPESENSE_ADMIN_API_KEY. The collection is dropped and rebuilt on every sync so deleted or renamed pages don’t linger as stale results — if you hand-tune the collection’s settings, reapply them after a build.
search: {
provider: "typesense",
typesense: {
host: "xyz.a1.typesense.net",
collection: "docs",
searchApiKey: "YOUR_SEARCH_ONLY_KEY", // public
// port + protocol default to 443 / https
},
}
Mixedbread
Semantic search via Mixedbread. Queries are proxied through a generated /api/search endpoint that holds your key, so this provider requires server output (deployment.output: "server"). The endpoint reads MIXEDBREAD_API_KEY. Sync your content to the store with the Mixedbread CLI in your build, e.g. mxbai vs sync <STORE_ID> ./content --ci.
search: {
provider: "mixedbread",
mixedbread: {
storeId: "YOUR_STORE_ID",
},
}
Disabling search
search: {
provider: "none",
}
Excluding pages
Only indexable pages are searched. A page is left out of the index when it sets search.exclude in frontmatter:
search:
exclude: true
Hidden pages are also excluded by default. To index them anyway, opt in:
search: {
indexing: { includeHiddenPages: true },
}