Search
On this page
Search
BX Sites ships one search provider by default and can be pointed at others
via bxsites.json's searchProvider -
search: true/false stays the master on/off switch regardless of which
provider is active.
Local (the default)
BX Sites' search is fully static and client-side - the same approach
mkdocs uses by default: an index built once at
build time, and lunr.js doing the actual searching
in the visitor's browser. There's no server, database, or external search
service involved.
How it works
- At
buildtime,SearchIndexerwalks every non-hidden page and writessite/search-index.json: one entry per page with itstitle,url, frontmattertags, the text of every heading on the page, and a truncated plain-text copy of its body (HTML tags stripped). - Each theme's
search.bxmpartial renders a search box;layout.bxmonly includes it (and thelunr.js+ sharedsearch.jsscripts) whenbxsites.json'ssearchistrueandsearchProvider.provideris"local"(the default - see Other providers below for what changes with a different one). - In the browser, the shared
assets/search.jswidget fetchessearch-index.jsononce, builds alunrindex from it (titleweighted highest, then frontmattertags, thenheadings, then plain body text), and re-searches it on every keystroke - no network round-trip per query.
Keyboard shortcuts
/focuses the search box from anywhere on the page (unless you're already typing in another field) - the same convention mkdocs-material uses.- Cmd/Ctrl+K also focuses it, from anywhere - including while typing in
another field - the convention Algolia DocSearch, Pagefind, VitePress and
Docusaurus all share. The search box shows a small
Ctrl K/⌘Khint (platform-detected) so it's discoverable. Escapecloses the results dropdown and blurs the search box.
Cmd/Ctrl+K works the same way for every provider - local's own widget
binds it directly, algolia gets it for free from DocSearch itself
(keyboardShortcuts defaults to true), and pagefind gets it wired up
by layout.bxm since PagefindUI doesn't bind it on its own.
Turning it off
{ "search": false }
Skips building search-index.json entirely, and skips the search box, the
vendored lunr.js script, and the shared search.js widget in every
rendered page - a project with search off ships nothing search-related at
all. This
is the master switch - it applies no matter which searchProvider is
configured.
Rebuilding just the index
bxSites search-index
Useful if you only need to refresh search-index.json - build already
does this as one of its own steps, so you don't need to run this
separately after a normal build. Only runs for providers that use the local
index ("local", and any provider bx-sites doesn't otherwise know about) -
it's a no-op (skipped: true) when searchProvider.provider is "algolia"
or "pagefind", since neither ever uses it.
Algolia
Set searchProvider.provider to "algolia" to swap the search box for
Algolia DocSearch - the same
crawler-hosted search mkdocs-material, VitePress, Starlight and Docusaurus
all support:
{
"search": true,
"searchProvider": {
"provider": "algolia",
"algolia": {
"appId": "ABC123",
"apiKey": "a1b2c3d4e5f6...",
"indexName": "my-docs",
"insights": false
}
}
}
appId, apiKey and indexName are required - apiKey is the
search-only public API key DocSearch gives you (never an admin key; it's
shipped straight into every rendered page). insights (false by default)
turns on DocSearch's own click/conversion analytics.
With algolia active:
- No
search-index.jsonis built, and the sharedlunr.js/search.jswidget isn't shipped - Algolia serves results from its own hosted index, populated by DocSearch's crawler or your own Algolia Crawler config, not by anything BX Sites writes at build time. You still need to register the site with DocSearch (or run your own crawler) separately - BX Sites only wires up the client widget. - Each built-in theme instead renders an empty
#bxsites-search-algoliacontainer, andlayout.bxmloads@docsearch/css/@docsearch/jsfrom jsDelivr and callsdocsearch({...})against it - DocSearch renders its own search button and modal into that container.
Pagefind
Set searchProvider.provider to "pagefind" to swap the search box for
Pagefind - another fully static/no-server search
engine, but indexed from the built site/ HTML rather than crawled like
Algolia:
{
"search": true,
"searchProvider": {
"provider": "pagefind",
"pagefind": { "bin": "pagefind", "options": [] }
}
}
Both pagefind keys are optional - bin (default "pagefind") is the
executable name/path, resolved against PATH when it's a bare name;
options is an array of extra raw CLI flags passed straight through (e.g.
["--exclude-selectors", ".no-index"]).
With pagefind active:
- The
pagefindCLI must already be installed and onPATH- BX Sites shells out to it (there's no BoxLang-native binding, the same reasonlastUpdated/gh-deployshell out togit), it doesn't install it for you. See Pagefind's installation docs. UnlikelastUpdated, a missing/failing binary fails thebuildloudly (BxSites.PagefindFailed) rather than degrading silently - shipping a site whose configured search provider doesn't work is worse than a failed build. - Right after every doc tree (main + versions + locales) is written and
sitemap.xml/llms.txtare generated, BX Sites runspagefind --site <siteDir> [...options]against the entire builtsite/- so a multi-version/multi-locale site gets everything indexed in one pass, unlike bx-sites' own per-treesearch-index.json. Pagefind writes its own bundle straight intosite/pagefind/- self-hosted, no CDN involved. - No
search-index.jsonis built, and the sharedlunr.js/search.jswidget isn't shipped (same asalgolia) - andbxSites search-indexis a no-op for the same reason (see above). - Each built-in theme renders an empty
#bxsites-search-pagefindcontainer, andlayout.bxmloadssite/pagefind/pagefind-ui.{css,js}and callsnew PagefindUI({...})against it - Pagefind renders its own inline search box and results into that container.
Other search providers
searchProvider.provider isn't limited to "local"/"algolia"/"pagefind" -
any other value is accepted by bxsites.json as-is (BX Sites' own config
validation only checks the three providers above). There's no plugin hook
for this one - the built-in themes simply render nothing for an
unrecognized provider name, and wiring up a fourth search service
(Meilisearch, Typesense, etc.) is a project-level
theme override: copy a built-in theme into
your project's own theme/ folder and add your provider's markup/scripts
to its layout.bxm/search.bxm, reading siteConfig.searchProvider to
decide when to render them - searchProviderName eq "..." branches for
the mount point in search.bxm, matching branches in layout.bxm for its
CSS/JS, and (if it isn't crawler-hosted like Algolia) whatever indexing
step that product needs against site/ after build - the same shape
this module's own layout.bxm/BuildPipeline.bx already use for
algolia/pagefind.