Theme blocks
A theme block is a block in the editor that's only available when a specific theme is active. Themes contribute these for layout primitives that match their visual style — magazine-style hero, services grid for corporate sites, etc.
Theme blocks complement the core blocks (which are always available regardless of theme) and the embed blocks (provided by the always-on flexweg-embeds mu-plugin).
When you see them
The editor's / inserter groups blocks by category. When the active theme registers any blocks, they appear in their own group at the bottom of the list, namespaced with the theme id:
/ ← inserter opens
Text ▶
Media ▶
Layout ▶
Embed ▶
Magazine ▶ ← theme-specific group (when magazine is active)
magazine/hero-split
magazine/most-read
magazine/promo-card
If you switch themes, the group changes. The default theme contributes no theme blocks; magazine and corporate each contribute several.
What the built-in themes contribute
default theme
No theme blocks. Uses only core blocks for content composition.
magazine theme
| Block | Purpose |
|---|---|
magazine/hero-split | Two-column hero — image left, headline + lead right. Used at the top of feature articles or static-page homes. |
magazine/most-read | Sidebar widget listing the most-read posts (data driven by posts.json + view counts when the search plugin is enabled, otherwise sorted by recency). |
magazine/promo-card | Compact promotional card — image + heading + CTA. Suitable for newsletter sign-ups, related products. |
→ See Magazine theme reference for full attribute lists.
corporate theme
| Block | Purpose |
|---|---|
corporate/hero-overlay | Full-bleed hero with image background, eyebrow + title + subtitle + dual CTAs. The flagship home block. |
corporate/hero-split | Two-column hero (image one side, copy the other). Variant for sub-pages. |
corporate/services-grid | 3-column card grid — title, description, icon. Used for "Our services" sections. |
corporate/cta-banner | Full-width call-to-action band. Title + button. |
corporate/testimonials | Testimonial slider / grid (variant configurable: glass cards or solid colour panel). |
corporate/trust-bar | Strip of client logos. |
corporate/stats-grid | Number-led stats (e.g. "500+ clients", "99.9% uptime"). |
corporate/feature-stack | Vertical stack of icon + title + description. |
corporate/contact-info | Address + phone + hours. |
corporate/contact-form | Embedded contact form with formspree-or-mailto submit (configurable). |
→ See Corporate theme reference for full attribute lists.
Inserting a theme block
Type / → look for the theme-named group → pick the block. The block inserts with default attributes from the theme's manifest. Each block's defaults give you a sensible starting point that you customise via the right-sidebar Block tab.
For example, inserting corporate/services-grid gives you 3 placeholder service cards. Use the inspector to:
- Change the section heading + subheading
- Add / remove / reorder cards
- Customize each card's icon (from a Material Symbols Outlined picker), title, description
Theme blocks are atom blocks in Tiptap — non-editable directly in the document. All editing happens in the inspector.
What happens at publish time
Theme blocks are stored in the post's markdown as <div data-cms-block="<theme>/<block>" data-attrs="<base64-encoded-json>"></div> markers. The publisher:
- Renders the post's body markdown to HTML via marked + DOMPurify
- Runs the theme's
post.html.bodyfilter — the filter scans for the theme's markers, decodes their attrs, and replaces each marker with the block's actual HTML output (rendered via the block'srender(attrs, env)function) - The result is the final post HTML with full theme block content baked in
This means theme blocks are rendered at publish time, not at runtime. Visitors don't see any markers; they see the finished HTML.
Switching themes
Theme blocks are scoped to the theme that registered them. If you've inserted magazine/hero-split in a post and switch to corporate:
- The marker stays in the markdown
- corporate's
post.html.bodyfilter doesn't recognise the magazine namespace → marker passes through unchanged - The published HTML contains
<div data-cms-block="magazine/hero-split" data-attrs="…"></div>— visitors see an empty div
To clean up after a theme switch:
- Re-edit affected posts — the editor shows the unknown block as "unrecognised block" with a delete button
- Or use
flexweg-importplugin's "Find and replace" feature (if you have it enabled) to bulk-strip markers
The editor itself does recognise blocks from any registered theme namespace (because the magazine theme is still loaded as bundled / external). But it tags them as "from a different theme" and disables in-place editing.
Per-theme inspector UX
Each block manifest declares an inspector component that renders in the right sidebar's Block tab when the block is selected. Themes ship sophisticated inspectors:
- A service card editor for
corporate/services-gridwith add/remove/reorder + icon picker - A testimonial list editor for
corporate/testimonialswith author + role + quote - A post selector for
magazine/most-read(or "auto" mode) - A media picker for hero blocks' background images
The inspector reads the block's current attrs, displays a form, calls editor.commands.updateAttributes() to mutate. Tiptap re-renders the block visually.
Authoring theme blocks
For theme authors, the contract is:
// In your theme manifest:
blocks: [
{
id: "my-theme/hero",
titleKey: "blocks.hero.title",
icon: SomeLucideIcon,
category: "layout",
attrSchema: { /* default attrs shape */ },
render: (attrs, env) => "<section>...</section>", // publish-time HTML
inspector: HeroInspector, // sidebar UI
extensions: [HeroNode], // optional Tiptap node
},
],
→ Full guide: Block authoring
Continue
- Themes overview — switching themes
- Magazine theme reference — every magazine block in detail
- Corporate theme reference — every corporate block in detail
- Block authoring — for theme authors