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
| Provider | Block ID | Output |
|---|---|---|
| YouTube | flexweg-embeds/youtube | <iframe src="https://www.youtube.com/embed/<id>"> |
| Vimeo | flexweg-embeds/vimeo | <iframe src="https://player.vimeo.com/video/<id>"> |
| Twitter / X | flexweg-embeds/twitter | <blockquote class="twitter-tweet">…</blockquote> + widgets.js |
| Spotify | flexweg-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:
transformBodyHtml(html)resets the Set, scans for<div data-cms-embed="...">markers, replaces each with the provider's embed HTML, and adds theproviderIdto the Set.getDetectedHeadStyles()returns the baseline embed CSS — emitted once if any provider was detected, regardless of which.getDetectedBodyScripts()returns one<script>tag per provider that needs JS — currently only Twitter shipswidgets.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.bodyfilter, 1page.head.extrafilter, 1page.body.endfilter - 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.