Skip to main content

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

BlockPurpose
magazine/hero-splitTwo-column hero — image left, headline + lead right. Used at the top of feature articles or static-page homes.
magazine/most-readSidebar 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-cardCompact promotional card — image + heading + CTA. Suitable for newsletter sign-ups, related products.

→ See Magazine theme reference for full attribute lists.

corporate theme

BlockPurpose
corporate/hero-overlayFull-bleed hero with image background, eyebrow + title + subtitle + dual CTAs. The flagship home block.
corporate/hero-splitTwo-column hero (image one side, copy the other). Variant for sub-pages.
corporate/services-grid3-column card grid — title, description, icon. Used for "Our services" sections.
corporate/cta-bannerFull-width call-to-action band. Title + button.
corporate/testimonialsTestimonial slider / grid (variant configurable: glass cards or solid colour panel).
corporate/trust-barStrip of client logos.
corporate/stats-gridNumber-led stats (e.g. "500+ clients", "99.9% uptime").
corporate/feature-stackVertical stack of icon + title + description.
corporate/contact-infoAddress + phone + hours.
corporate/contact-formEmbedded 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:

  1. Renders the post's body markdown to HTML via marked + DOMPurify
  2. Runs the theme's post.html.body filter — 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's render(attrs, env) function)
  3. 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.body filter 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:

  1. Re-edit affected posts — the editor shows the unknown block as "unrecognised block" with a delete button
  2. Or use flexweg-import plugin'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-grid with add/remove/reorder + icon picker
  • A testimonial list editor for corporate/testimonials with 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