Skip to main content

Installing external themes

External themes are third-party themes packaged as .zip files. You upload the ZIP via the admin's Install theme button; the admin extracts it client-side, uploads files to Flexweg, and registers the theme so it shows up alongside the built-ins.

The same mechanism powers all themes — the built-in default / magazine / corporate themes are technically built as "external bundles" by the admin's build pipeline. Third-party themes work identically.

Prerequisites​

You need a .zip file containing:

  • manifest.json — installation metadata (id, name, version, apiVersion, entry)
  • bundle.js — the ESM bundle with the theme's React templates
  • theme.css — the compiled CSS for the public site
  • README.md — optional, surfaced via "Learn more"

→ For theme authors: see External theme tutorial for the format.

The install flow​

  1. Open Themes in the sidebar.
  2. Click Install theme at the top.
  3. The install modal opens. Click Choose ZIP or drag a .zip onto the picker.
  4. The admin extracts client-side via JSZip. Validation runs:
    • manifest.json exists
    • manifest.id is sanitised lowercase ASCII + dashes
    • manifest.apiVersion is in [FLEXWEG_API_MIN_VERSION, FLEXWEG_API_VERSION]
    • bundle.js (or whatever manifest.entry points to) exists in the ZIP
    • Not already installed — if the id matches an existing entry, the install fails with "duplicate id" — uninstall the existing one first
  5. If validation passes, every file in the ZIP is uploaded to /admin/themes/<id>/<file> on Flexweg (via flexwegApi.uploadFile).
  6. The Firestore registry doc (settings/externalRegistry) is updated with the new entry.
  7. The admin reloads. The theme appears as a card in the Themes list.

Activating the new theme​

Installing doesn't make the theme active — it just makes it available. Click the theme's card → Activate to switch.

The activation flow is the same as for built-ins: confirmation modal → regenerate every published page with the new templates + CSS. See Switching themes.

What gets uploaded where​

Your Flexweg site
└── /admin/themes/<theme-id>/
├── manifest.json ← copied from the ZIP, used by re-zip / re-install
├── bundle.js ← the ESM module — admin imports this at boot
├── theme.css ← uploaded as /theme-assets/<id>.css when synced
└── README.md ← optional

The bundle.js is the runtime artefact — the admin's external loader does import("/admin/themes/<id>/bundle.js") to load the theme's manifest.

The theme.css is the theme's baseline CSS — uploaded to /theme-assets/<id>.css when you click Sync theme assets (or when you switch to the theme). Without that step, the public site would 404 on the theme CSS.

Upgrading an installed theme​

There's no native "Update" button. To upgrade:

  1. Uninstall the existing version (Themes → click the theme's card → Uninstall)
  2. Install the new ZIP (Themes → Install theme → pick the new ZIP)

Theme settings (your saved colour overrides, logo, etc.) are stored at settings/site.themeConfigs.<theme-id> in Firestore — they're keyed by theme id, so re-installing the same id preserves them. Uninstalling doesn't delete the config.

If the new version has a breaking change to the config shape, the theme should provide migration in its manifest's register() callback or settings page.

Manual install (without the upload UI)​

If you can't use the upload UI (e.g. you're scripting deploys), you can do it manually:

  1. Unzip the theme into a folder named <theme-id>/
  2. Upload that folder to /admin/themes/<theme-id>/ on Flexweg (via Flexweg's File manager or the Files API)
  3. Add the entry to settings/externalRegistry in Firestore (via the Firebase Console):
// settings/externalRegistry
{
themes: [
{
"id": "your-theme-id",
"version": "1.0.0",
"apiVersion": "1.0.0",
"entryPath": "themes/your-theme-id/bundle.js"
}
],
// existing plugins...
}
  1. Reload the admin. The theme appears.

Common install errors​

"duplicate id"​

A theme with this id is already installed. Uninstall it first, then try again.

"manifest.json missing"​

The ZIP doesn't have a manifest.json at its root. The author may have zipped a wrapper folder by mistake. Common pattern: mytheme-1.0.0/manifest.json instead of manifest.json. The admin's install detects single-folder wrapping and peels it transparently — but if there are multiple wrapping folders, manual flattening is needed.

"bundle.js missing"​

Same — the entry file declared in manifest.json (defaults to bundle.js) is not in the ZIP. The author probably forgot to run their build before zipping.

"apiVersion outside supported range"​

The theme was built against an admin API version your admin doesn't support. Either:

  • The theme is too old — the author needs to release a newer version
  • The theme is too new — your admin is older than the theme requires; upgrade the admin

"Authentication failed. Check your Flexweg API key in Settings."​

The Flexweg API rejected the upload. Check Settings → General → Flexweg API key (or re-run the first-run setup if config is broken).

Continue​