Skip to main content

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

FilePath on FlexwegPurpose
search-index.json/search-index.jsonThe index — title, slug, excerpt, category, tags, date for every online post
search.js/search.jsRuntime 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 contentposts only or posts + pages. Default posts + 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.js and /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 countTitle-onlyTitle + excerptTitle + 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.baseUrl is 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:

  1. Run Themes → Regenerate site → All HTML pages to drop the <script> from existing pages.
  2. Delete /search.js + /search-index.json via Flexweg's file manager.

When changes apply

ActionFiles updatedLatency
Publish a post/search-index.json (when content changed)Within seconds
Unpublish / deleteSameWithin seconds
Edit indexed fields in settingsNone automaticallyRun 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.deleted actions; page.body.end filter; registerRegenerationTarget
  • Storage paths: /search-index.json, /search.js
  • Runtime size: ~10 KB compiled
  • Translations: 7 locales

Continue