Stedefast

Modules

Search

2 min read

Search

/module-search adds instant full-text search to your site. At build time it generates a JSON search index from all your content. The island loads it lazily and searches entirely client-side using MiniSearch — no Cloudflare Worker or server required.

Installation

pnpm add @stedefast/module-search

Setup

// stedefast.config.ts
import { defineConfig } from "@stedefast/core";
import { SearchModule } from "@stedefast/module-search";

export default defineConfig({
  // ...
  modules: [
    SearchModule({
      fields: ["title", "description", "tags"],
      hotkey: true,
    }),
  ],
});

No Cloudflare bindings are required — search is entirely static.

Adding the search button to your theme

Import SearchButton from the module and place it in your header:

// theme/partials/header.tsx
import { SearchButton } from "@stedefast/module-search/island";

export default function Header({ site }) {
  return (
    <header>
      <a href="/">{site.title}</a>
      <SearchButton label="Search…" />
    </header>
  );
}

Keyboard shortcut

With hotkey: true (the default), visitors can open the search dialog with ⌘K on Mac or Ctrl+K on Windows/Linux. Set hotkey: false to disable it.

How it works

  1. Build timebuildStaticExport iterates all content nodes, strips HTML from excerpts, builds a MiniSearch index, and writes it to dist/data/search/index.json
  2. Page load — nothing is loaded upfront; the index is fetched lazily the first time the dialog opens
  3. Search — MiniSearch performs fuzzy, prefix-aware full-text search entirely in the browser with title matches ranked higher than body text

Config reference

Option Type Default Description
fields string[] ["title","description","tags"] Front matter fields to include in the index
hotkey boolean true Enable ⌘K / Ctrl+K shortcut
excludeTypes string[] [] Content types to exclude from the index
excerptLength number 500 Max characters of body text indexed per page

Performance

For a 500-page site, index.json is typically 200–400KB. To reduce index size on large sites:

  • Set excerptLength: 0 to exclude body text (index titles and descriptions only)
  • Use excludeTypes: ["note"] to skip content types that don't need to be searchable

Notes

  • The search dialog is accessible: it uses <dialog>, aria-modal, and a focus trap
  • Keyboard navigation: arrow keys move between results, Enter navigates, Escape closes
  • For very large sites (>1000 pages), a future D1-backed Worker variant is planned — see the writing a module guide for how to build your own