Search

Every site uses local search by default. The Search button opens a native modal dialog, as /, Ctrl+K and Cmd+K do. / only opens search outside a text field. Typing searches the language and version being read, by heading: each result is a section with its page title and a snippet of the original text.

ArrowDown moves focus from the field to the first result; ArrowUp starts at the last. Further arrow presses move through the results. Enter opens the focused result, or the first result while focus remains in the field. Tab follows the native button order, and Escape closes the dialog.

Matching and options

Search uses MiniSearch with prefix and typo matching. A query requires every word, ignoring case and accents, so cafe finds “Café” and instalation finds “Installation”. Native word segmentation supports languages without spaces. Heading and page-title matches receive more weight than body matches; snippets preserve the original characters.

Configure local search in markupress.config.ts:

import { defineConfig } from 'markupress';

export default defineConfig({
  search: { limit: 20, fuzzy: 0.2, prefix: true },
});

limit is a positive whole number. fuzzy is a fraction between 0 and 1 that controls the allowed spelling difference relative to the word's length; false or 0 disables it. prefix is a boolean. The example shows the defaults. Unknown options and invalid values fail configuration validation.

Set search: false in site configuration to omit the search button and generated indexes.

Exclude content

To keep a page readable and in navigation while excluding it from search, set its Markdown front matter:

---
search: false
---

To exclude part of a page, put data-search-ignore on an HTML element:

<div data-search-ignore>
This text remains visible but is absent from the search index.
</div>

The attribute excludes the element's whole subtree, including headings. Literal code examples remain indexed. Exclusion controls discovery, not access: the content remains on the public page.

The index

build writes each version's index beside its pages at /v/<id>/_search.json, and the default version's at /_search.json. The root index links to the root's own pages. Entries contain a page title, heading, URL and section text. Development regenerates indexes after edits.

The dialog fetches its index on the first nonempty query and builds an in-memory search index. Subsequent queries reuse it. A failed fetch shows an unavailable message and retries on later input; an older request cannot replace a newer query. Sites with very large indexes should measure download size and browser indexing time for their own content.

Hosted search with Algolia

Choose provider: 'algolia' to query your Algolia or DocSearch index through the same localized dialog:

import { defineConfig } from 'markupress';

export default defineConfig({
  origin: 'https://docs.example.com',
  search: {
    provider: 'algolia',
    appId: 'YOURAPPID',
    apiKey: 'your-public-search-only-key',
    indexName: 'documentation',
    limit: 20,
    debounce: 150,
    timeout: 5000,
  },
});

Use a public search-only key. The browser receives it with the application ID and index name. Account approval, index creation, crawling and importing records belong to your Algolia setup. Hosted queries require a network connection; local search remains available by omitting the provider or setting provider: 'local'.

Every query includes language:<locale-id> and version:<version-id> as AND facet filters. Without locale configuration, language is the site's lang or en. Configure language and version in the index's attributesForFaceting, for example filterOnly(language) and filterOnly(version). Optional filters: 'audience:public' adds application filters while retaining both context filters. limit sets the maximum number of results; debounce is a nonnegative whole number of milliseconds and timeout is positive. Unknown options and local-only fuzzy/prefix options fail validation for this provider.

Records use the DocSearch hierarchy/content/URL fields plus language and version. Matching records can contain a string or array for these facets. The dialog shows plain text and snippets, filters unsafe or wrong-context destinations, and opens canonical production URLs on the current preview origin. Results must stay under the selected locale/version path. Query changes, closing the dialog and navigation discard pending results. Failed or timed-out requests show the localized unavailable message; later input retries.

Markupress adds https://<app-id>-dsn.algolia.net to MarkupKit's connect-src. Existing directives and report-only mode remain in effect. When no connection policy exists, the added directive also permits 'self' for native page requests. An explicit csp: false remains disabled. An explicit restrictive connection policy keeps its chosen sources plus the configured provider origin.

Import the generated records

With the hosted provider enabled, build writes /_algolia.json beneath the site's base. Development regenerates it after edits. It contains one record per indexed section and context, splitting long sections into records no larger than 10,000 UTF-8 JSON bytes to fit Algolia's smallest documented record limit. Content and graphemes are preserved across chunks. Each record includes a stable objectID derived from document identity, section identity, language, version and chunk number. A route rename retains object IDs when the explicit document id and section identities stay the same.

  1. Set origin to the production origin and build the site.
  2. Read the generated JSON array from the output directory and upload it with your Algolia dashboard or server-side indexing client.
  3. Configure searchableAttributes as hierarchy.lvl1, hierarchy.lvl2, content, and attributesForFaceting as filterOnly(language), filterOnly(version). Permit retrieval of hierarchy, content, URL and context fields and snippet retrieval of content.
  4. Replace the full index snapshot when importing, or explicitly remove stale object IDs. An upsert alone leaves deleted documents searchable. Keep administrative indexing keys in your deployment's secret store.

The export honors page and subtree exclusions. Source-fallback entries carry the selected catalog's language and version and link to its contextual page. Canonical URLs and alternate-language ownership remain tied to the original content. Index import is explicit; a local build does not upload records.

In a site layout

A layout that replaces the shell receives the version's index URL as its search prop and the resolved local settings as searchOptions. The optional hosted prop contains the resolved provider and catalog settings, or null for local search (see Layouts). The shipped theme uses Looma's ui-search-shell and ui-search-result-row. Its button stays hidden until its controller connects.

Edit this page

Last updated