Tangly v0.2 ships richer code blocks, page chrome, and more — see what's new

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:

md
## Rate limits

API calls are capped at 1000/min per key.

You can embed that section anywhere:

md
<Embed page="guides/billing" block="rate-limits" />

The block renders inline and links back to the source.

How block IDs are picked

  1. 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 limitsrate-limits

  2. 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:

Embed error:Block "status" not found on page "introduction". Available: tangly, what-you-get, what-you-write, built-for-agents, mintlify-compatible, whats-next.

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.
↑↓ navigate open esc close