Embedded blocks
Reuse content across pages with <Embed page="x" block="y" />.
~ 2 min read
Embedded blocks
Use <Embed> to render a labeled block from another page inline, without copy-pasting. The source page stays the canonical home; embeds re-render with whatever changes upstream.
Quick example
Suppose guides/billing.mdx has:
## Rate limits
API calls are capped at 1000/min per key.You can embed that section anywhere:
<Embed page="guides/billing" block="rate-limits" />The block renders inline and links back to the source.
How block IDs are picked
-
Auto from headings
Every heading gets an ID derived from its text using the same algorithm as
rehype-slug/ GitHub: lowercase, non-alphanumerics → hyphens, deduped per page.## Rate limits→rate-limits -
Explicit {#custom-id} markers
Override the auto ID by appending
{#my-id}to a heading, paragraph, or list item.md ## Service tiers {#tiers} Or label a paragraph: this one. {#highlight}
What gets embedded
A heading-anchored block extends from the heading through the next sibling or parent heading. So ## Rate limits (h2) closes at the next h2 or h1.
A {#paragraph-id} marker captures only that paragraph (until the next blank line or heading).
Embeds render Markdown only — no MDX components inside the embedded block. If you need component-rich content shared across pages, use a snippet (snippets/foo.mdx) and import it as a component instead.
Cycle detection
If page A embeds a block that itself embeds something on A, the build fails with a clear trail:
[tangly] Circular <Embed> detected: a#one → b#two → a#one.
Break the cycle by inlining one block or removing one embed.
Live example
Below is an <Embed> pulling the “Status” block from the Introduction page:
That’s the same content you’d see at /introduction#status.
Implementation
<Embed> resolves at build time. The manifest extracts every block from every page during build-manifest.ts; <Embed> looks up the block, sanitizes the Markdown via rehype-sanitize with a strict allow-list, and renders the HTML inline.
This means:
- Zero runtime cost — the embed is baked into the prerendered HTML.
- No client JS — embeds work without scripts loaded.
- Sanitized — block sources can’t smuggle
<script>or event-handler attributes into the embedding page.
When NOT to embed
- Snippet content (callouts, sample code) — use
snippets/instead. Snippets allow MDX/components. - Versioned changes — embeds resolve at build time against the current source; they’re not historical.
- Cross-version content — when versioning lands, each version will get its own block index.