Search solutions for static sites
Options for adding search to documentation, blogs and static-site gardens —
client-side (index shipped to the browser) vs server-side (self-hosted
engine). Researched 2026-09-13 for garden.h; resource numbers are indicative,
verify before committing. Elasticsearch excluded by policy (JVM, GB-class
RAM for what we need).
Requirement scope: English + Polish as targets. Czech/Slovak are tracked
only as reference implementations — how full West-Slavic language support
looks when adding Polish elsewhere. Shortlist under that scope:
Pagefind (EN porter2 + PL Snowball, zero backend) and ZBSearch
(Orama fork by the original team; EN built-in; PL via a custom stemmer —
see below) client-side; Meilisearch (explicit eng+pol locales,
typo tolerance) or Typesense (EN+PL stemming, ~30 MB idle) server-side.
lunr-languages works but downloads the whole index.
Client-side (no backend)
- FlexSearch — what Quartz search uses. ~5 KB, index built at
runtime from a JSON corpus. Fast prefix matching; no stemming, no
diacritics folding. Fine ≤ a few hundred notes. - lunr + lunr-languages — classic SSG search (Hugo/MkDocs heritage).
Full index downloaded upfront (bandwidth grows linearly — bad for large
sites). Polish: ✅ (lunr-languages ships a PL stemmer + stopwords);
Czech/Slovak: ❌. - Pagefind — Rust CLI indexes built HTML, ships chunked WASM index;
searches load only the needed chunks (~100 KB payload for most sites,
MDN-scale < 300 KB). Zero config, per-section results, filters.
Multilingual vialangattribute; stemming follows Snowball →
Polish: ✅ (added in the 1.x Snowball upgrade), Czech: ✅, Slovak: ❌.
Best “just works at scale” option; no quartz-community plugin (see
Quartz search). - Orama / ZBSearch (~2 KB; Orama formerly Lyra) — full-text + vector +
hybrid in browser or server, BM25, typo tolerance. Prefers ZBSearch:
fork by the original Orama team (ex co-founder/CTO Michele Riva + the
whole engineering team) after the VC fallout — same engine, no business
incentives, actively developed (v4.0.0). 32 languages with tokenizer +
stopwords + stemmer: Czech ✅ (port of Lucene’s CzechStemmer, Dolamic &
Savoy 2009 — suffix tables + palatalization, dependency-free), Slovak ✅
(ZBSearch-only: hand-written light stemmer; explains why the Czech one is
not a stand-in and how diacritic folding interacts with stemming — the
blueprint for a Polish stemmer), Slovenian ✅, Polish ❌ in both.
Gotcha: Orama’sskcode is Sanskrit, not Slovak (ZBSearch fixed
this withsvk). Custom stemmers are first-class:
Stemmer = (word) => stringpassed tocomponents.tokenizer; most
built-ins are Snowball-compiled JS, so a Polish stemmer is
snowball compiler → polish.sbl → JS plug-in. Strong middle-ground
option for EN+PL.
Server-side (self-hosted engines)
- Meilisearch — Rust, single binary, typo-tolerant, ~instant. Charabia
tokenizer;localessetting (ISO-639-3) explicitly lists pol, ces AND
slk — the only engine surveyed with explicit Slovak support. Hybrid
keyword+vector search built in. Homelab-fit: container or binary,
moderate RAM (~100–200 MB class). - Typesense — C++, single binary, ~30 MB idle, RAM-mapped, very fast.
localeaccepts any ISO 639-1 incl. pl/cs/sk → ICU normalization +
collation; Snowball stemming → Czech ✅, Polish ✅ (Snowball PL exists),
Slovak ❌ (no Snowball algorithm — gets normalization only). Dictionary
stemming (stem_dictionary) as escape hatch. - Algolia — SaaS only (no self-host). Free 10k searches/mo, 50k records;
OSS projects get 200k/mo with attribution. Excellent DX (InstantSearch
UI), but data leaves your infrastructure — conflicts with the self-hosting
premise. Reference point, not a candidate. - QMD — for the agent/wiki side (not website visitors): BM25 +
vectors + LLM rerank in one local binary. Different problem, same family.
Language support matrix (PL / CS / SK)
| Engine | Polish | Czech | Slovak |
|---|---|---|---|
| Snowball (reference) | ✅ | ✅ | ❌ |
| lunr-languages | ✅ | ❌ | ❌ |
| Pagefind (Snowball) | ✅ | ✅ | ❌ |
| Orama | ❌ | ✅ | ❌ |
| Meilisearch locales | ✅ | ✅ | ✅ |
| Typesense (ICU+Snowball) | ✅ | ✅ | ~ (norm. only) |
| FlexSearch (any lang) | ❌ no stemming at all | ❌ | ❌ |
Czech is the best-represented West-Slavic language (official Snowball
algorithm + Orama full support) — for our EN+PL scope it serves purely as a
reference: the reference implementation pattern to copy when adding Polish
support anywhere. Slovak is covered only by Meilisearch.
Choosing
- Small garden/blog (≤ ~500 pages): client-side. Pagefind if stems/multilingual
matter; stock FlexSearch (Quartz search) if prefix search suffices. - Docs portal / work: Meilisearch (PL+CS+SK explicit, typo tolerance,
hybrid) or Typesense (leanest RAM) behind the reverse proxy. - Inflected-Polish matching on a static site without a server: pre-stem the
corpus at build time — see Polish stemming and lemmatization.
Cross-refs: Quartz search, Polish stemming and lemmatization,
QMD, RAG, Local LM roadmap.