flexweg-search
The flexweg-search plugin adds client-side full-text search to your public site. It generates a JSON index at publish time and ships a tiny runtime script that opens a search modal anywhere your theme exposes a [data-cms-search] trigger.
No server, no third-party service — works great up to ~5 000 posts. For bigger sites consider Algolia or similar via custom code.
What it generates
| File | Path on Flexweg | Purpose |
|---|---|---|
search-index.json | /search-index.json | The index — title, slug, excerpt, category, tags, date for every online post |
search.js | /search.js | Runtime script: opens the modal, filters the index, renders results |
The runtime is bundled into the plugin's source as search-runtime.js and imported via ?raw. So search.js is a single self-contained vanilla-JS file — no external dependencies, no module loading.
Settings
/settings/plugin/flexweg-search exposes:
- Indexed content —
posts onlyorposts + pages. Defaultposts + pages. - Excluded categories — categories whose posts should not appear in the index (e.g. legal pages, internal updates).
- Search fields — checkboxes for
title,excerpt,body content. Title is always on. Body is off by default to keep the index size down. - Body field truncation — when "body content" is on, posts are indexed with the first N characters of plain-text content (default 1 000). Long posts fall off after the cutoff.
- Hotkey — keyboard shortcut to open the modal globally (default
Cmd+K/Ctrl+K). - Force regenerate — re-uploads both
/search.jsand/search-index.json.
How it hooks in
Three lifecycle actions keep the index in sync:
api.addAction("publish.complete", regenerate);
api.addAction("post.unpublished", regenerate);
api.addAction("post.deleted", regenerate);
Plus one filter that injects <script src="/search.js" defer> on every published page via page.body.end. Themes don't need to know about the search plugin — enabling it is enough to wire the runtime everywhere.
The plugin also registers a Regeneration target for Themes → Regenerate site → Search.
Triggering the modal
Themes call out a search trigger via either:
- A
[data-cms-search]element — the runtime listens for clicks and opens the modal. - The configured hotkey — works site-wide.
Example trigger in a theme's header:
<button data-cms-search aria-label="Search">
<SearchIcon />
</button>
The modal is injected into the DOM by search.js the first time it opens, so themes don't need any boilerplate for the modal markup.
Index size
The index size scales linearly with post count. Rough numbers:
| Post count | Title-only | Title + excerpt | Title + excerpt + body (1 000 chars) |
|---|---|---|---|
| 100 | ~10 KB | ~30 KB | ~150 KB |
| 1 000 | ~100 KB | ~300 KB | ~1.5 MB |
| 5 000 | ~500 KB | ~1.5 MB | ~7 MB |
For sites at the upper end:
- Disable body indexing (only index title + excerpt).
- Exclude categories that don't need to be searchable.
- Beyond ~5 000 posts, switch to a hosted search service.
Hash optimisation
config.lastIndexHash and config.lastRuntimeHash track what was last uploaded. On every regeneration:
- The new index is hashed; if it matches
lastIndexHash, the upload is skipped. - The runtime file is hashed once at boot; if it matches
lastRuntimeHash, the upload is skipped.
So a publish that doesn't actually change the indexable corpus (e.g. a typo fix in a non-indexed field) is a no-op for the search plugin.
When skipped
settings.baseUrlis empty — search-result links need an absolute origin. Logs a warning and exits.
Toggle
Plugins → Plugins tab → flexweg-search card → Enable / Disable.
Disabling does not delete files already on Flexweg. The runtime <script> injection stops on the next page render, so newly-published pages won't load /search.js. Already-published pages still reference the file until they're re-rendered.
To clean up after a disable:
- Run Themes → Regenerate site → All HTML pages to drop the
<script>from existing pages. - Delete
/search.js+/search-index.jsonvia Flexweg's file manager.
When changes apply
| Action | Files updated | Latency |
|---|---|---|
| Publish a post | /search-index.json (when content changed) | Within seconds |
| Unpublish / delete | Same | Within seconds |
| Edit indexed fields in settings | None automatically | Run Force regenerate |
| Switch admin language | /search.js (UI strings inside the runtime) | Run Force regenerate |
Internal details
- Source:
src/plugins/flexweg-search/ - External in production builds: yes
- Hooks used:
publish.complete,post.unpublished,post.deletedactions;page.body.endfilter;registerRegenerationTarget - Storage paths:
/search-index.json,/search.js - Runtime size: ~10 KB compiled
- Translations: 7 locales
Continue
- Site features: Search — how visitors interact with the modal
- Themes overview — wiring
[data-cms-search]triggers in custom themes - Hooks reference