Your data

Docs and help centers

Records and settings for documentation and help centers: one record per section, so a search lands on the heading that answers it.

Readers search docs for a task or a term, and want the part of the page that covers it. Give each section its own record, and a search lands on the right heading with the matching words highlighted. The search on these docs works this way.

Records

One record per section, for each ## and ### heading, plus one for each page's introduction:

{
  "objectID": "settings#synonyms",
  "page": "Search settings",
  "section": "Synonyms",
  "group": "Guides",
  "url": "/docs/settings#synonyms",
  "text": "synonyms are groups of words or phrases that match each other: searching for any of them finds the others…",
  "code": "",
  "summary": "synonyms are groups of words or phrases that match each other: searching for any of them finds the others, ranked slightly below the word itself.",
  "level": 3,
  "order": 29
}
  • Sections, not pages. A whole page matches nearly any search about its topic, and opens at its top. A section matches what it covers, opens at its heading, and fits in the 16 KB a searchable field holds.
  • url ends with the heading's anchor, so a result opens at the section.
  • text is plain text, without Markdown or HTML, so highlights and excerpts read well. Code goes in its own field, searched with less weight.
  • summary is the section's first sentence, to show when the match was in the heading.
  • level and order put a page's introduction and main sections ahead of its subsections when results tie, in reading order.

Build the records from your Markdown or HTML when the site builds, and upload them with replaceAll on each deploy, so renamed and removed sections leave search.

Settings

{
  searchableAttributes: [
    { field: "section", weight: 8 },
    { field: "page", weight: 4 },
    { field: "text", weight: 2 },
    { field: "code", weight: 1 },
  ],
  facetFields: [],
  filterFields: ["group"],
  customRanking: [
    { field: "level", direction: "asc" },
    { field: "order", direction: "asc" },
  ],
  synonyms: [["delete", "remove"], ["api key", "secret key"], ["autocomplete", "search as you type"]],
  didYouMean: true,
  languages: ["en"],
}
  • Headings first. They're short and say what a section is about, and the first two searchable fields hold 1 KB each, which suits them.
  • Synonyms for your product's terms and the words readers use instead, such as "remove" for "delete".
  • group as a filter lets a search box offer choices like "Guides only".

SearchDialog is the pattern docs sites use: a button that opens a search dialog, which ⌘K (Ctrl+K elsewhere) and / open too.

import { SearchDialog } from "@findlane/browser/react"

export function DocsSearch() {
  return (
    <SearchDialog
      publicId="pub_your_public_id"
      placeholder="Search the docs"
      fields={["page", "section", "url", "summary"]}
      hrefTemplate="{url}"
    />
  )
}

Show each result as "page › section", with an excerpt from Snippet around the matched words (see Highlights).

Questions and keywords

Every word in a search must match. languages leaves out common words such as "the" and "a", but not question words, so "how do I delete a record" only finds sections that also contain "how" and "do". Keywords such as "delete record" work best, and a placeholder can suggest them.

Private docs

Everything browser search can read is public. Docs for signed-in customers belong in their own index, searched from your server with a search key after you check who's asking.