Skip to main content

Plugins overview

Plugins extend Flexweg CMS by hooking into the publish flow, contributing editor blocks, dashboard cards, settings pages, and translations. The system is heavily inspired by WordPress's filters / actions model — plugins register callbacks that the admin invokes at well-known lifecycle points.

Three categories of plugins

CategoryWhere they liveToggleableAlways loaded
Built-insrc/plugins/ (in admin source), shipped with the build✓ — Plugins page on/off toggleOnly if enabled
Must-use (mu)src/mu-plugins/ (in admin source)✗ — no toggleAlways
ExternalUploaded ZIP, lives in /admin/plugins/<id>/ on Flexweg✓ — same as built-ins, plus uninstallableOnly if enabled and installed

In production, built-in and external plugins are loaded the same way — both are pre-compiled ESM bundles fetched at boot via the runtime loader. The difference is only how they got there: built-ins ship with the admin's dist/, externals are uploaded by the user.

Must-use plugins are bundled directly into the admin's main JS chunk and run unconditionally — they're for behaviour the CMS can't meaningfully run without.

Built-in plugins

Five plugins ship with Flexweg CMS as opt-in (toggleable) extensions:

PluginWhat it does
core-seoTwitter Card meta tags + <meta name="generator"> hint
flexweg-sitemapsXML sitemaps + sitemap-index + Google News + robots.txt
flexweg-rssSite-wide RSS 2.0 feed + per-category feeds
flexweg-archivesDate-based archive pages (year / month / week)
flexweg-searchStatic search index + client-side search modal

Open Plugins in the sidebar to toggle them on/off. Each has its own settings page accessible from the Settings sidebar (tabs auto-appear when the plugin is enabled).

Must-use plugins

Six plugins are always-on, no toggle:

PluginWhat it does
flexweg-faviconFavicon generation from a single uploaded image (PNG, ICO, SVG, PWA manifest)
flexweg-blocksFirst-party editor blocks: Custom HTML, Columns
flexweg-custom-codeSite-wide injection: head + body-end zones for analytics / chat / fonts
flexweg-embedsEmbed blocks: YouTube, Vimeo, Twitter/X, Spotify
flexweg-metricsStorage + Firestore usage cards on the dashboard
flexweg-importBulk content import from markdown / WordPress XML

These appear in the Plugins page under the Must-use tab, but with no enable / disable toggle — just a "Must-use" badge and the Configure / Learn more buttons.

What plugins can do

A plugin's manifest registers any of these via pluginApi:

Filters

Transform a value as it passes through. Multiple filters on the same hook chain together (priority-ordered).

api.addFilter<string>("page.head.extra", (head, baseProps) => {
return head + '<meta name="x-my-plugin" content="hello" />';
});

Common filter hooks:

  • post.markdown.before — modify Markdown before rendering
  • post.html.body — modify the post's rendered HTML
  • post.template.props — modify props passed to the active theme's template
  • page.head.extra — inject markup into <head>
  • page.body.end — inject markup before </body>
  • menu.json.resolved — mutate the resolved menu structure before upload

Hooks reference for the full list

Actions

Side-effects on lifecycle events. Multiple actions on the same hook all run.

api.addAction("publish.complete", (post, ctx) => {
console.log(`Published: ${post.id}`);
// Update some external system, regenerate a related file, etc.
});

Common action hooks:

  • publish.before / publish.after / publish.complete
  • post.unpublished
  • post.deleted

Editor blocks

Plugins can contribute blocks that show up in the post / page editor's / inserter. The flexweg-blocks mu-plugin contributes Custom HTML + Columns; flexweg-embeds contributes the four embed providers; themes contribute their layout blocks.

Block authoring

Dashboard cards

Plugins can register cards that appear on the home dashboard. flexweg-metrics contributes the Storage + Firestore cards.

Dashboard cards

Settings pages

Plugins can declare a settings page reachable at /admin/#/settings/plugin/<plugin-id>. The framework wraps the plugin's component in a route, merges saved config with defaults, and provides a save(next) callback.

Plugin settings pages

Translations

Plugins can ship i18n bundles in their manifest's i18n field. The framework loads them into a dedicated i18next namespace named after the plugin id, so plugin UI calls useTranslation("<plugin-id>") to scope its keys without colliding with admin keys.

[Plugin i18n]

Toggling plugins

Open Plugins in the sidebar. The page has two tabs:

  • Plugins — built-in + external plugins, with Enable / Disable buttons
  • Must-use — must-use plugins, with a Must-use badge and no toggle

Clicking Disable on an enabled plugin:

  1. Updates settings.enabledPlugins[id] = false in Firestore
  2. The settings subscription fires, applyPluginRegistration re-runs
  3. The registry resets, all enabled plugins re-register, the disabled one is excluded
  4. Any dashboard card / hook handler / block from that plugin disappears immediately

Some side-effects don't auto-undo. For example, if core-seo was emitting Twitter Card tags on every published page, disabling it doesn't strip those tags from already-published HTML. New publishes will skip them. Use Themes → Regenerate site → All HTML to retroactively re-render every page without the disabled plugin's output.

Configuring plugins

If a plugin has a settings page, a tab appears in the Settings layout's tab strip when the plugin is enabled. The settings live at /admin/#/settings/plugin/<plugin-id>.

Plugin configs are stored at settings/site.pluginConfigs.<plugin-id> in Firestore. Each plugin's config is preserved when you toggle it off — re-enabling restores the saved config. Resetting requires explicitly clearing the field via Firestore (or via a "Reset" button if the plugin's settings page provides one).

Installing third-party plugins

To install an external plugin from a .zip:

  1. Plugins → Install plugin → pick the ZIP.
  2. The admin extracts client-side (via JSZip), validates the manifest, uploads files to /admin/plugins/<id>/ on Flexweg.
  3. Updates the Firestore registry (settings/externalRegistry).
  4. Reloads. The plugin appears in the Plugins list alongside built-ins.

Installing external plugins

Uninstalling plugins

You can uninstall:

  • External plugins (uploaded by you)
  • Built-in plugins (the architecture treats them as externalised at build time, so they're uninstallable too)

Click the Uninstall button on the plugin's card (red, only visible for non-mu-plugins). Confirms, then:

  1. Removes the entry from the Firestore registry
  2. Deletes /admin/plugins/<id>/ from Flexweg
  3. Reloads

If you later regret uninstalling a built-in, the Reinstall bundled defaults flow restores it. See Uninstalling.

Authoring custom plugins

If you want to build your own plugin, see:

Continue