Files
documentation-docusaurus/references/dedicated-homepage-local-search.md
2026-08-14 12:32:08 +00:00

5.8 KiB

Dedicated Docusaurus Homepage + Native Local Search

Use this pattern when a docs-only Docusaurus site needs a product/showcase homepage and search that runs entirely from static build artifacts.

Route separation

A docs plugin with routeBasePath: '/' can coexist with src/pages/index.tsx, but no doc may also claim /.

---
id: intro
title: Documentation overview
slug: /overview
---

After moving the intro, re-check Markdown links. A link such as ./methodology from /overview/ resolves under /overview/; use ../methodology, a Docusaurus doc link, or an explicit base-path URL.

Keep footer/navbar links explicit:

{label: 'Home', to: '/'},
{label: 'Overview', to: '/overview'},

Local search without hosted services

Install at the root of a root-only pnpm workspace:

pnpm add -w @easyops-cn/docusaurus-search-local

Configure it as a theme, not a plugin:

themes: [
  [
    require.resolve('@easyops-cn/docusaurus-search-local'),
    {
      hashed: true,
      indexDocs: true,
      indexBlog: false,
      indexPages: true,
      docsRouteBasePath: ['/', 'clients'],
      language: ['en'],
      highlightSearchTermsOnTargetPage: true,
      explicitSearchResultPath: true,
      searchResultLimits: 12,
      searchResultContextMaxLength: 120,
    },
  ],
],

docsRouteBasePath must cover every docs plugin whose content should be searchable. Do not add Algolia credentials, crawler configuration, Ask AI, or another network service when the requirement is browser-local search.

One SearchBar only

The theme supplies the navbar @theme/SearchBar. Avoid importing and rendering another SearchBar on the homepage: duplicate instances can contend for the singleton autocomplete/index lifecycle and leave the UI with input text but no fetched index or result overlay.

Use a landing-page trigger instead:

const openSearch = () => {
  const input = document.querySelector<HTMLInputElement>('.navbar__search-input');
  if (input) {
    input.focus();
    return;
  }
  document.dispatchEvent(
    new KeyboardEvent('keydown', {key: 'k', ctrlKey: true, bubbles: true}),
  );
};

<button type="button" onClick={openSearch}>
  Search documentation <kbd>Ctrl/⌘ + K</kbd>
</button>

Keep the navbar field itself as the sole search implementation.

Generated homepage metrics

Do not hard-code changing registry statistics into JSX. Extend the canonical content generator to emit an ignored TypeScript module:

summary = ROOT / "src" / "generated" / "registrySummary.ts"
summary.parent.mkdir(parents=True, exist_ok=True)
summary.write_text(
    "export const registrySummary = "
    + json.dumps({"total": total, "researched": researched}, separators=(",", ":"))
    + " as const;\n",
    encoding="utf-8",
)

Add /src/generated to .gitignore, run generation before both tsc and docusaurus build, and import from @site/src/generated/registrySummary.

Government-portal visual adaptation

When adapting an official public-sector site, copy the visual grammar rather than cloning its page:

  • derive a small token set from the real site (navy, action blue, pale neutral background, white surfaces, restrained borders);
  • use a locally installed font package to avoid runtime font dependencies;
  • reuse only appropriate official marks with clear provenance;
  • reproduce hierarchy: narrow institutional strip, white utility navigation, large editorial hero, evidence/status blocks, and dark institutional footer;
  • preserve the user's established geometry preference (for Lego, globally force border-radius: 0 even if the source site uses pills);
  • provide dark-mode equivalents without replacing the light-first official character.

For the VSSA pattern, the useful observed tokens were approximately navy #091a5a, action blue #0b66e4, pale background #f3f6f9, and Public Sans. The official coat-of-arms SVG was suitable as a local navbar asset.

Public page unchanged: distinguish an untriggered workflow from a failed workflow

When a user cannot see locally completed changes, do not begin with runner debugging. Establish the publication chain in order:

  1. git status --short --branch — are the changes still uncommitted?
  2. git rev-list --left-right --count HEAD...origin/main — were they pushed?
  3. Read the newest Actions run and compare its head_sha to the intended commit.
  4. If the newest run still points to the previous SHA, Actions did not fail; the new workflow was never triggered. Commit and push.
  5. Wait for the run attached to the intended SHA to reach completed success.
  6. Fetch the public page with Cache-Control: no-cache and a query marker such as ?verify=<short-sha>; assert a distinctive new heading or asset, not only HTTP 200.
  7. Verify the public search-index.json contains representative searchable terms, then run a real browser query and open a result.

This sequence prevents two common false diagnoses: treating a successful local build as a deployment, and treating an old successful Actions run as evidence that a new change was published.

Verification gate

Run all of these before declaring completion:

pnpm validate
pnpm build
test -s build/search-index.json

Then programmatically inspect the index for representative terms from:

  1. the main docs plugin;
  2. every secondary docs plugin;
  3. the custom homepage when indexPages: true.

Finally, serve the production build and verify in a browser:

  • desktop and mobile homepage layout;
  • navbar search and keyboard shortcut;
  • a query produces visible results;
  • opening a result reaches the expected dossier route;
  • square geometry and responsive navigation remain intact.

Publication is complete only after commit/push, a successful Gitea Actions run, and HTTP/content verification of the public homepage, overview, representative result route, and search index.