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 content — posts 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​