Skip to main content

flexweg-embeds

flexweg-embeds adds YouTube / Vimeo / Twitter / Spotify blocks to the post editor. Each block is a Tiptap atom node that round-trips through Markdown as a <div data-cms-embed="..."> marker, then gets swapped for the real embed HTML at publish time.

It's must-use because disabling it would silently turn every embed in every post into a plain <div> on the next regeneration — visually broken with no fallback.

What it provides

ProviderBlock IDOutput
YouTubeflexweg-embeds/youtube<iframe src="https://www.youtube.com/embed/<id>">
Vimeoflexweg-embeds/vimeo<iframe src="https://player.vimeo.com/video/<id>">
Twitter / Xflexweg-embeds/twitter<blockquote class="twitter-tweet">…</blockquote> + widgets.js
Spotifyflexweg-embeds/spotify<iframe src="https://open.spotify.com/embed/...">

All inserter blocks land in the Embed category. The user pastes the URL into the inline input on the empty atom node — provider parsing is per-provider in src/mu-plugins/flexweg-embeds/providers.ts.

See Editor → Embeds for visitor-facing details.

How it hooks in

api.registerBlock({ /* per-provider, 4 blocks total */ });
api.addFilter<string>("post.html.body", transformBodyHtml);
api.addFilter<string>("page.head.extra", emitBaselineEmbedCss);
api.addFilter<string>("page.body.end", emitDetectedProviderScripts);

Per-page detection

transforms.ts keeps a module-level Set<providerId> that tracks which providers appeared on the current page:

  1. transformBodyHtml(html) resets the Set, scans for <div data-cms-embed="..."> markers, replaces each with the provider's embed HTML, and adds the providerId to the Set.
  2. getDetectedHeadStyles() returns the baseline embed CSS — emitted once if any provider was detected, regardless of which.
  3. getDetectedBodyScripts() returns one <script> tag per provider that needs JS — currently only Twitter ships widgets.js. YouTube/Vimeo/Spotify are iframe-only, no body scripts.

The publisher pipeline runs these three filters back-to-back for the same page (post.html.body → page.head.extra → page.body.end → render), so the Set stays consistent. Resetting at the start of transformBodyHtml prevents previous-page state from bleeding into the next.

Round-trip via Markdown

The editor's tiptap-markdown is configured with html: true specifically to make <div data-cms-embed="..."> round-trip through parseHTML / renderHTML without being escaped. DOMPurify's default ALLOWED_ATTR lets data-* attributes through, so the markers survive sanitisation and reach transformBodyHtml intact.

Editor styles

ensureAdminEmbedStyles() runs at module-load to inject the baseline embed CSS into the admin document — same baseline as the public site. This makes iframe-based embed previews (YouTube, Vimeo, Spotify) honour their 16:9 aspect ratio inside the editor exactly as they will on the published page.

Adding a new provider

One entry in src/mu-plugins/flexweg-embeds/providers.ts:

{
providerId: "newProvider",
titleKey: "newProvider.title",
icon: NewProviderIcon,
parseUrl: (url) => /* return { id } | null */,
renderHtml: (id) => `<iframe src="...${id}"></iframe>`,
bodyScript: undefined, // or `<script src="..." async></script>`
}

Plus i18n keys for the title under each locale's bundle. The block factory and per-page detection wire up automatically.

Internal details

  • Source: src/mu-plugins/flexweg-embeds/
  • Hooks used: 4 block registrations, 1 post.html.body filter, 1 page.head.extra filter, 1 page.body.end filter
  • Translations: 7 locales (provider labels)

Why must-use

Disabling unregisters the four blocks. Existing posts with embeds would render their <div data-cms-embed="..."> markers as plain <div>s on the next regeneration — visually broken, no graceful fallback. The marker carries the URL but no HTML the browser would do anything useful with.

Continue