Integrations

Starlight

Make ZBSearch the search engine of your Astro Starlight site.

@zbsearch/plugin-starlight replaces Starlight's built-in Pagefind search with a dialog powered by ZBSearch.

The index is built from Starlight's own content collection, so slugs, drafts and front matter come from Starlight rather than from a second interpretation of your files. Everything runs in the visitor's browser: no service, no API key, no query leaving the page.

Installation

The search box is a React island, so the site needs Astro's React renderer alongside the plugin:

npm install @zbsearch/plugin-starlight @astrojs/react react react-dom

Starlight itself is framework-agnostic, but this integration reuses the same React search box the Docusaurus plugin renders, so the two behave identically. The plugin registers @astrojs/react for you if your site has not already.

Then add the plugin to Starlight:

import starlight from '@astrojs/starlight';
import zbsearch from '@zbsearch/plugin-starlight';
import { defineConfig } from 'astro/config';

export default defineConfig({
  integrations: [
    starlight({
      title: 'My docs',
      plugins: [zbsearch()],
    }),
  ],
});

That is the whole setup. Pagefind is disabled automatically, and Starlight's Search component is taken over. Start the site and press ⌘K.

What gets indexed

Every page in the docs collection is split into one record per heading, so a result lands on the exact section that matched rather than the top of a long page.

A record carries four searchable fields:

FieldContents
titleTitle of the page, from front matter
sectionThe heading the chunk was taken from
hierarchyFolders the page lives in, its title, and the enclosing headings
contentThe prose beneath that heading

Front matter, fenced code, JSX and MDX imports are stripped before indexing. Permalinks and category labels travel with each record but are deliberately not tokenized, so a query never matches a URL fragment.

Pages marked draft: true are skipped unless you ask for them.

Options

zbsearch({
  excludeRoutes: ['/internal/**'],
  maxResults: 12,
});

Content

OptionDefaultDescription
language'english'Language used to tokenize and stem the index
excludeRoutes[]Routes to leave out, with * and ** wildcards
categoryLabel'Docs'Label shown next to a result's page title
indexDraftsfalseIndex pages marked draft: true

Ranking

OptionDefaultDescription
maxResults12Maximum number of hits shown at once
boost{ title: 4, section: 3, hierarchy: 1.5, content: 1 }Per-property ranking weights
tolerance1Edit distance tolerated per term
threshold0Minimum share of query terms a document must match
snippetLength140Maximum length of the excerpt under a hit

Interface

OptionDefaultDescription
recentSearchestrueRemember and replay recently opened results
hotkeystrueBind the ⌘K / Ctrl+K and / shortcuts
searchButtonLabel'Search'Text of the header button
placeholder'Search documentation…'Placeholder of the search input
labels{}Copy overrides for the dialog

Every string in the dialog can be replaced through labels, which is how you localise it.

How it works

The plugin injects a prerendered route at /zbsearch-index.json. That endpoint reads the docs collection with getCollection, splits each page into records, builds a ZBSearch index and serializes it.

Because it is a real Astro route, the dev server answers it on demand and the production build writes it to dist/ as a static file - the same index in both, with no second code path to keep in sync.

The search box fetches it the first time a visitor shows intent to search. Neither the index nor ZBSearch itself weighs on the initial page load.

The route respects your base, build.format and trailingSlash settings, so result links match the URLs Starlight actually generates.

Styling

Every colour, radius and font in the search box is a --zbs-* custom property, so you can re-theme it from your own CSS without overriding a single rule:

:root {
  --zbs-accent: #0aa;
  --zbs-radius: 8px;
}

The dialog follows Starlight's theme switcher, including its auto setting, by reading the data-theme attribute Starlight sets on <html>.

The full list of properties lives in the search box documentation.

Keyboard

KeysAction
⌘K, Ctrl+K, /Open the search box
Move through the results, wrapping at both ends
Home EndJump to the first or last result
EnterOpen the selected result
-clickOpen a result in a new tab
EscClose

Troubleshooting

The header shows no search button. Starlight only renders a search component when Pagefind is enabled or the Search component has been overridden. Check that the plugin is inside Starlight's plugins array rather than Astro's integrations.

The build fails resolving @astrojs/react. The React renderer has to be installed in the site itself, not only as a transitive dependency. Run npm install @astrojs/react react react-dom.

Search finds nothing. Open /zbsearch-index.json directly: it should list your pages. If it is empty, check that your pages live in the docs collection and are not all marked draft.

On this page