Search plugin for Steno that generates a client-side search index by scanning the final rendered HTML of every page in your build.
# content/.steno/config.yml
plugins:
- jsr:@steno/plugin-searchplugins:
- package: jsr:@steno/plugin-search
options:
indexPath: search-index.json
excerptLength: 180
exclude:
- /404
- /drafts| Option | Type | Default | Description |
|---|---|---|---|
indexPath |
string |
search-index.json |
Output-relative path for the generated index file. |
excerptLength |
number |
180 |
Maximum length, in characters, of each entry's excerpt. |
exclude |
string[] |
[] |
Route prefixes to omit from the index (exact string prefix match against the page route). |
Once installed, the plugin automatically generates the search index directly in your configured output directory on every build:
dist/
├── index.html
├── posts/
│ └── hello-world.html
└── search-index.json <-- Generated Automatically
The generated file is a JSON array, sorted by title, with one entry per
indexed page:
[
{
"title": "Hello World",
"route": "/posts/hello-world",
"excerpt": "This is the opening text of the page, stripped of markup and truncated to the configured excerpt length...",
"headings": ["Getting started", "Next steps"]
}
]titleis the content of the page's<title>tag, or an empty string if none is present.routeis the page's public route, derived from its output path (index.html->/,foo/index.html->/foo/,foo.html->/foo).excerptis the page's rendered body with<script>/<style>blocks and all remaining HTML tags stripped, whitespace collapsed, and truncated toexcerptLengthcharacters.headingslists the text content of every<h2>and<h3>on the page, in document order.
The plugin hooks into Steno's beforeBuild, afterPage, and afterBuild
lifecycle:
- In
beforeBuild, it captures the build's output directory so page paths can be resolved to public routes. - In
afterPage, it inspects each rendered page's HTML directly — no re-parsing of Markdown or other content sources — extracting a title, headings, and excerpt, and computing its route. Pages matching anexcludeprefix are skipped. - In
afterBuild, it sorts the accumulated entries by title and writes the JSON index toindexPathin the output directory.
Because it works from rendered HTML rather than a specific content source, this plugin indexes any Steno site regardless of how pages are authored.
Actually using this index client-side — wiring up a search-as-you-type box, fuzzy matching, keyboard navigation, and so on — is left to your theme or site. This plugin's job ends at producing the data file; how you fetch and render it in the browser is up to you.
MIT