The search box is part of the documentation reading experience, not a site-wide feature. A page gets one when its page type is a documentation or API type and that type has a search index bound to it — those two conditions are what put the search modal in the page and what tell the search script there is an index to load. A marketing page built from your own master template has no search box unless you add one yourself.
Search arrives with the rest of the documentation reading experience: the sidebar, the breadcrumbs, the table of contents and the search modal are all rendered by the same shell.
Everything on this page ships on every plan, including Free, and none of it costs you a setting beyond creating the index.
What a reader sees
At desktop width the trigger sits in the documentation header: a magnifier, the word Search, and a Ctrl K chip.
Both the word and the chip are hidden below the lg breakpoint, so on a phone the same control renders as an icon-only button. It keeps an accessible name (aria-label="Search") either way, so a screen reader announces it as Search at every width.
Clicking it — or pressing the shortcut — opens a fullscreen modal over the page. The input is focused for you, carries the placeholder Type to search…, and shows an ESC hint at its right edge. Results appear as you type, after a short pause, once the query is at least two characters long.
Each result row shows the page type it came from, any labels on the page, the result title, and a snippet of the matching section with your search words highlighted. Above the list is a count — Search results (7). When nothing matches, the modal says No results found for “your query” — the query echoed back verbatim — with the subtitle Try different keywords or check your spelling.
The keyboard shortcut
Ctrl + K opens search from anywhere on the page. That is the binding on every platform, macOS included — it is not Cmd + K.
Once the modal is open:
| Key | What it does |
|---|---|
| Ctrl + K | Open search, clear the input and show recent searches |
| Down / Up | Move the highlight through the result list |
| Enter | Open the highlighted result |
| Esc | Close the modal |
The shortcut is bound on the modal, which the documentation shell always renders, rather than on the trigger button, which lives in the header. That matters if you replace the header: a custom documentation header that leaves the trigger out still has working search — readers just have to know the shortcut. If you would rather keep the button, include Leed’s own trigger partial in your header, in its full or its compact icon-only form.
Recent searches
When the input is empty the modal shows a Recent searches list instead of results, so reopening search puts a reader back where they were. Clicking an entry re-runs that query.
The list holds the seven most recent queries and lives entirely in the reader’s own browser, in localStorage under the key leedSearchHistory. It is never sent to Leed, never reaches your analytics, and disappears when the reader clears their site data or opens a private window. Each row is timestamped in the browser too, so entries read Searched 4 minutes ago.
A query is remembered when the reader stops typing for two seconds, or when they click a result. Queries shorter than two characters are never stored.
What the prebuilt index contains
The index is built at the end of every deployment, from the pages belonging to the page types your search index covers.
The part worth understanding is that a document is a heading section, not a page. Leed renders each page, splits it at every heading from h1 through h5, and indexes the text between one heading and the next as its own record. That is why a result title reads Page title - Heading and why clicking it lands on that heading’s anchor rather than the top of the page. Sections with no text under them are dropped, and each section’s text is capped at 5,000 characters — a section longer than that is indexed up to the cap and truncated with an ellipsis.
flowchart TD
subgraph build ["At build time, on every deployment"]
A[Deployment] --> B["Pages in the index's page types"]
B --> C["Split at every h1-h5 heading"]
C --> D["static/search/id-index.json"]
C --> E["static/search/id-documents.json"]
end
subgraph read ["In the reader's browser"]
F["Ctrl+K, or the Search button"] --> G["Fetch both files at ?v=content-hash"]
G --> H["Lunr ranks the sections"]
H --> I["Result reads: Page title - Heading"]
I --> J["Jump to that heading's anchor"]
end
D --> G
E --> G
Ranking is Lunr’s, over four weighted fields:
| Field | Weight | Source |
|---|---|---|
title | ×10 | The page title plus the section’s heading |
labels | ×6 | The names of the labels on the page |
pageTypeName | ×3 | The name of the page type the page belongs to |
content | ×1 | The section’s text, capped at 5,000 characters |
A search returns at most ten results. Because the title field carries both the page title and the heading and is weighted ten times the body, a query that matches a heading almost always outranks the same words buried in prose — which is the behavior you want, and a reason to write headings a reader would actually search for. Headings are anchored down to level five, so results have somewhere precise to land; the heading syntax page covers how those anchors are generated.
The index ships as two static JSON files, both under /static/search/ and both named for the index’s id:
/static/search/<searchIndexId>-index.json?v=<indexHash>
/static/search/<searchIndexId>-documents.json?v=<documentsHash>The ?v= values are content hashes of the two files, written into the page head at build time. A reader whose browser cached last week’s index gets a different URL after the next deployment and fetches the new one, so a stale index is not a failure mode you have to think about.
On some plans the build ships a different search client behind this same box, with different behavior and different failure states; live documentation search is where that client and the plans it applies to are documented.
Deciding what readers can find
A search index is a named set of page types. It is created in Settings → Search Indexes with a Title and a Page Types list, and bound to a documentation set through that page type’s documentation configuration — one field, searchIndexId, described in the documentation configuration reference.
That indirection is what gives you control over scope:
- One index per set keeps your product documentation and your API reference searching separately, even though both sit on the same domain.
- One index shared by two sets gives readers a single search across both — the same index can be bound to any number of page types.
- Folding a posts type into a docs index makes release notes and tutorials findable from the documentation search box without moving them into the docs.
A page type that is in no index at all is skipped by the indexer entirely, so leaving a type out is how you keep it out of reader search. Creating an index and choosing the page types it covers walks through the flow. An index edit is a pending change like any other and reaches readers on the next publish.
When the search box does nothing
Troubleshooting checklist, in the order worth checking
- The page type has no search index bound. Without a
searchIndexIdin its documentation configuration, the page head carries no index reference and the search script stops before it fetches anything. This is by far the most common cause. - The page is not a documentation or API page. The search modal is rendered by the documentation shell. A page using your own master template has no modal, so there is nothing for the shortcut to open.
- The index exists, but nothing in its page types is published. An index over an empty set of pages builds successfully and finds nothing.
- The index or the content changed, but the site has not been rebuilt. Index membership is resolved at build time. Publish, then search.
- A custom documentation header removed the trigger. Ctrl + K still opens the modal — the binding is on the modal, not the button. Add Leed’s trigger partial back to your header to restore the visible control.
- A page has no headings at all. Sections are cut at headings; a page whose body is one unbroken run of prose is indexed as a single section, and a page whose sections are all empty produces no documents.
If none of those explains it, the troubleshooting index collects the symptoms that turn out not to be about search at all. Readers whose AI clients want to search your documentation come in through a different door entirely — the Docs MCP — which has its own index and its own rules.