SveltePress default theme provides built-in Local Search powered by Pagefind out of the box with zero configuration. In addition, the default theme supports Algolia DocSearch through docsearch and custom search components through search, including @sveltepress/meilisearch.
Local Search (Default)
Local search is enabled by default with zero configuration. When building your documentation site (pnpm build), SveltePress automatically runs Pagefind to index all static HTML pages and bundles the search assets into /pagefind/.
Features
- Zero-config: Works immediately without external API keys or remote indexing services.
- Offline & Static: Runs entirely in the browser via WebAssembly, fast and privacy-friendly.
- Multi-locale support: Automatically detects
<html lang="...">and filters search queries by the active page language. - Keyboard navigation: Opens via
Cmd+K(macOS) orCtrl+K(Windows/Linux), navigate with Arrow keys, select with Enter, close with Escape. - Development notice: In development mode (
pnpm dev), searching displays an informative notice explaining that the full index is created during production build.
Disabling Local Search
If you want to disable search entirely:
import { const defaultTheme: ThemeDefaultdefaultTheme } from '@sveltepress/theme-default'
import { const sveltepress: (options?: SveltepressVitePluginOptions) => PluginOptionsveltepress } from '@sveltepress/vite'
import { function defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
UserConfig
object, or a function that returns it.
The function receives a
ConfigEnv
object.
defineConfig } from 'vite'
export default function defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
UserConfig
object, or a function that returns it.
The function receives a
ConfigEnv
object.
defineConfig({
UserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use.
plugins: [
function sveltepress(options?: SveltepressVitePluginOptions): PluginOptionsveltepress({
SveltepressVitePluginOptions.theme?: ResolvedTheme | undefinedtheme: function defaultTheme(themeOptions?: DefaultThemeOptions | undefined): ResolvedThemedefaultTheme({
DefaultThemeOptions.search?: string | boolean | Component<{}, {}, string> | undefinedsearch: false,
}),
}),
],
}) You can also disable the build-time indexer in the Vite plugin:
import { const sveltepress: (options?: SveltepressVitePluginOptions) => PluginOptionsveltepress } from '@sveltepress/vite'
function sveltepress(options?: SveltepressVitePluginOptions): PluginOptionsveltepress({
SveltepressVitePluginOptions.pagefind?: boolean | PagefindOptions | undefinedOptions for Pagefind static local search indexing.
Set to false to disable automatic post-build indexing.
pagefind: false,
}) Algolia DocSearch
Pass a docsearch config object to defaultTheme to use Algolia DocSearch instead of the default local search in the navbar.
Required fields are appId, apiKey, and indexName. Every other DocSearch option is also accepted.
import { const defaultTheme: ThemeDefaultdefaultTheme } from '@sveltepress/theme-default'
import { const sveltepress: (options?: SveltepressVitePluginOptions) => PluginOptionsveltepress } from '@sveltepress/vite'
import { function defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
UserConfig
object, or a function that returns it.
The function receives a
ConfigEnv
object.
defineConfig } from 'vite'
export default function defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
UserConfig
object, or a function that returns it.
The function receives a
ConfigEnv
object.
defineConfig({
UserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use.
plugins: [
function sveltepress(options?: SveltepressVitePluginOptions): PluginOptionsveltepress({
SveltepressVitePluginOptions.theme?: ResolvedTheme | undefinedtheme: function defaultTheme(themeOptions?: DefaultThemeOptions | undefined): ResolvedThemedefaultTheme({
DefaultThemeOptions.docsearch?: Omit<DocSearchProps, "theme" | "container"> | undefineddocsearch: {
appId: stringAlgolia application id used by the search client.
appId: 'YOUR_APP_ID',
apiKey: stringPublic api key with search permissions for the index.
apiKey: 'YOUR_SEARCH_API_KEY',
indexName?: string | undefinedName of the algolia index to query.
indexName: 'YOUR_INDEX_NAME',
},
}),
}),
],
}) DocSearch is free for open-source documentation sites. Apply at docsearch.algolia.com.
Meilisearch
@sveltepress/meilisearch is the supported Meilisearch search component. Install it first:
npm install --save @sveltepress/meilisearch yarn add @sveltepress/meilisearch pnpm install @sveltepress/meilisearch bun add @sveltepress/meilisearch Create a wrapper that provides your Meilisearch connection settings:
<script lang="ts">
import
type Search = SvelteComponent<Record<string, any>, any, any>
const Search: LegacyComponentType
Search from '@sveltepress/meilisearch/Search.svelte'
</script>
<const Search: LegacyComponentTypeSearch
host: stringhost="https://search.example.com"
apiKey: stringapiKey="YOUR_SEARCH_ONLY_KEY"
indexName: stringindexName="docs"
/> Then pass the wrapper path to the default theme's custom-search hook:
import { const defaultTheme: ThemeDefaultdefaultTheme } from '@sveltepress/theme-default'
function defaultTheme(themeOptions?: DefaultThemeOptions | undefined): ResolvedThemedefaultTheme({
DefaultThemeOptions.search?: string | boolean | Component<{}, {}, string> | undefinedsearch: '/src/lib/MeilisearchSearch.svelte',
}) The component queries an existing Meilisearch index; it does not build the index for you. Each record should provide id, title, content, and either url or path. Use a search-only API key in browser code.
A custom search source path is bundled into static production builds: the theme resolves the configured .svelte path at build time and loads it as a lazy chunk, so no extra runtime configuration is needed. Configure the wrapper as a source path (as above). Passing a component object directly is not supported — theme options are serialized to JSON for the client, so objects are dropped. If a production deployment shows no search, make sure search is a source-path string.
For another search provider, implement the same search hook with your own Svelte wrapper. Search precedence is: custom search component > explicit docsearch > default LocalSearch.
Search across locales and versions
When your site combines i18n locales with version management, search stays per-locale and per-version, and the crawler-facing outputs follow the same URL scheme the site serves (/, /zh/, /bn/, /v/<id>/…, /zh/v/<id>/…).
Locale-aware search
Each locale carries its own theme options, so give each locale its own DocSearch index — the pre-i18n documentation site used one index per language:
import { const defaultTheme: ThemeDefaultdefaultTheme } from '@sveltepress/theme-default'
import { const sveltepress: (options?: SveltepressVitePluginOptions) => PluginOptionsveltepress } from '@sveltepress/vite'
import { function defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
UserConfig
object, or a function that returns it.
The function receives a
ConfigEnv
object.
defineConfig } from 'vite'
export default function defineConfig(config: UserConfig): UserConfig (+5 overloads)Type helper to make it easier to use vite.config.ts
accepts a direct
UserConfig
object, or a function that returns it.
The function receives a
ConfigEnv
object.
defineConfig({
UserConfig.plugins?: PluginOption[] | undefinedArray of vite plugins to use.
plugins: [
function sveltepress(options?: SveltepressVitePluginOptions): PluginOptionsveltepress({
SveltepressVitePluginOptions.theme?: ResolvedTheme | undefinedtheme: function defaultTheme(themeOptions?: DefaultThemeOptions | undefined): ResolvedThemedefaultTheme({
// Site-level options shared by every locale (logo, github, pwa, ...)
}),
SveltepressVitePluginOptions.locales?: LocalesConfig<any> | undefinedMulti-locale site configuration keyed by URL prefix ('/' for the default
locale, '/zh/', '/bn/', ...). Each entry carries that locale's lang,
a user-facing label for the switcher, and its full theme options.
When omitted the site stays single-locale and behavior is unchanged.
locales: {
'/': {
LocaleConfig<any>.lang: stringBCP 47 language tag, e.g. 'en', 'zh-CN', 'bn'.
lang: 'en',
LocaleConfig<any>.label: stringUser-facing label rendered in the language switcher.
label: 'English',
LocaleConfig<any>.theme: anyThe locale's full theme options.
theme: {
docsearch: {
appId: string;
apiKey: string;
indexName: string;
}
docsearch: {
appId: stringappId: 'YOUR_APP_ID',
apiKey: stringapiKey: 'YOUR_SEARCH_API_KEY',
indexName: stringindexName: 'sveltepress',
},
},
},
'/zh/': {
LocaleConfig<any>.lang: stringBCP 47 language tag, e.g. 'en', 'zh-CN', 'bn'.
lang: 'zh',
LocaleConfig<any>.label: stringUser-facing label rendered in the language switcher.
label: '中文',
LocaleConfig<any>.theme: anyThe locale's full theme options.
theme: {
docsearch: {
appId: string;
apiKey: string;
indexName: string;
}
docsearch: {
appId: stringappId: 'YOUR_APP_ID',
apiKey: stringapiKey: 'YOUR_SEARCH_API_KEY',
indexName: stringindexName: 'cn',
},
},
},
},
}),
],
}) The Navbar remounts the DocSearch widget whenever the active index or version changes, so switching locale or version queries the right index.
Version-aware search
A version in the manifest may carry search metadata. While a reader is on that historical version's pages (/v/<id>/…), the theme switches DocSearch to the configured indexName and merges facetFilters into the query:
{
"versions": [
{
"id": "2026-08-28",
"search": {
"indexName": "sveltepress-v2026-08-28",
"facetFilters": ["version:2026-08-28"]
}
}
]
} Keep the crawler's facet tags in sync with this metadata.
Historical versions without a search object show "Search is not available for this documentation version." for DocSearch and custom search components only. Built-in Local Search still loads the frozen Pagefind index under that version's /pagefind/ path (via syncHistoricalPagefind on release) and does not require search metadata.
Crawling and result URLs
The generated sitemap.xml lists every locale's current pages and every eligible historical version page with hreflang alternates; EOL history is excluded unless the version opts out (noIndex: false), and every version page emits its own rel="canonical" link. Index records must point at these real, prefixed URLs — a Chinese record's url is /zh/guide/…, a frozen version's is /v/2026-08-28/guide/….
Custom search components
The Navbar renders a custom search component only when search is available for the current route, remounts it per version, and passes it two props:
version— the active version object ({ id, label, status, … }), or the current version on unprefixed pages.versionSearch— the version'ssearchmetadata ({ indexName?, facetFilters? }), ornull.
When your index stores multiple versions, filter results by the facets in versionSearch. Record URLs must be the full prefixed routes. No locale prop is passed: if you keep one index per locale, read the locale from location.pathname yourself.