Docs & Code Guide

Embed an architecture diagram in a GitHub README: markdown, Camo, and staying current

A GitHub README is the first page most engineers read about a system, and the diagram in it is usually the first thing to go stale. GitHub sanitizes README HTML aggressively, so of all the ways to show a diagram, exactly one can stay current without commits: an image by URL. Here is the markdown that works, what the Camo proxy does to it, and where the native Mermaid alternative fits.

8 min readFor maintainers who want the README diagram to match the code

See it as a diagram

Everything below, as a diagram you can edit. Describe yours and see it in seconds.

171/20003 credits left
Try:

No account needed · Editable canvas, not a picture

One route survives GitHub's sanitizer: an image by URL

GitHub runs rendered markdown through an HTML sanitizer before it reaches the page. Script tags, style tags, and iframes are removed outright, which rules out every interactive embed. What survives is a small allowlist that includes images, so the way to show a diagram that changes without commits is a plain markdown image pointing at a URL that re-renders when the diagram does.

A Datadef project shared public gets exactly that: a permanent image endpoint that reflects the current state of the diagram. Paste it into the README like any other image. The endpoint takes optional sizing parameters, format png or jpeg, width 600 to 4000, height 400 to 4000, and scale 1 to 3, with a default of 1600x1000 at 2x that is already sharp for a README.

![System architecture](https://datadef.io/api/embed/my-platform-a1b2c3d4)

<!-- Sized wider for a README that leads with the diagram -->
![System architecture](https://datadef.io/api/embed/my-platform-a1b2c3d4?width=2400&height=1400&scale=2)

What Camo does to your image URL

You will notice that the rendered README never loads your URL directly. GitHub rewrites every external image to its anonymizing proxy at camo.githubusercontent.com, so the reader's browser talks to GitHub, and GitHub fetches from the origin. This protects reader IP addresses from external servers, and it means update latency is decided by how Camo caches.

Camo caches by URL and honors the cache headers the origin sends. An origin that sends nothing can see its images cached for a very long time, which is where the folklore about READMEs showing week-old images comes from. Datadef serves the embed with Cache-Control max-age=300, so Camo re-checks the origin every five minutes and an edited diagram shows up in the README within minutes, not on the next commit.

If a copy ever seems stuck, Camo supports an explicit purge: send an HTTP PURGE request to the camo.githubusercontent.com URL you see in the rendered page (curl -X PURGE followed by that URL) and the next view re-fetches. One more timing note from the origin side: the first render after an edit is produced by a headless browser and can take a few seconds, then it is cached.

Relative paths, absolute URLs, and what live actually means

A relative path in README markdown, like docs/architecture.png, resolves to a file in the repository at the branch being viewed. That image is versioned with the code, which is tidy, and stale by construction: it only changes when someone regenerates the file and commits it. An absolute URL to a live endpoint inverts that trade. The README markdown never changes again; the pixels behind it do.

The two compose. Teams that want the diagram versioned per release can keep a committed export for tags and the live URL on the default branch, or regenerate the committed file in CI on merge. The CI recipe is covered in a living diagram from GitHub Actions.

Dark mode with the picture element

GitHub's sanitizer allows the picture element, and GitHub documents it as the supported way to show theme-aware images: a source tag with media set to prefers-color-scheme dark, and an img fallback. Readers on the dark theme get the dark asset, everyone else gets the fallback.

This works with any pair of image URLs, including one live URL plus a committed dark export, or two live URLs if you keep a dark variant of the diagram as its own shared project. Check how your diagram reads on the dark theme before deciding you need the second variant; a render that carries its own background often reads fine on both.

<picture>
  <source media="(prefers-color-scheme: dark)" srcset="docs/architecture-dark.png">
  <img alt="System architecture" src="https://datadef.io/api/embed/my-platform-a1b2c3d4">
</picture>

Native Mermaid: the in-repo alternative and where it stops

GitHub renders Mermaid natively in fenced code blocks, and for small flows it is the right tool: the diagram source lives in the same commit as the prose, reviews like code, and needs no external service. A sequence diagram of one handshake or a five-box flow belongs in a Mermaid fence.

The limit is layout. Mermaid computes it, and past roughly 20 nodes, or when you need zones, groupings, and column-level detail, generated layout stops being readable. That is the point where a rendered diagram with deliberate layout earns the external URL. The general markdown trade-offs, including this one, are covered in embed diagrams in Markdown.

Keeping the README diagram true after the code changes

The live URL removes the re-export and re-commit step, not the edit itself. When the architecture changes, someone or something still updates the diagram. The manual loop is fine for slow-moving systems: edit in Datadef, and every README showing the embed follows within minutes.

The automated loop uses the Datadef MCP server, registry name io.datadef/mcp. An agent connected to it, Claude Code or Cursor in practice, can read the repository and update the diagram to match: after a merge, on a schedule, or when asked during review. To be exact about the boundary: Datadef does not watch your repository and nothing detects code changes on its own. The regeneration is one agent call or one CI command away. Connecting an agent needs an API key, available on paid plans. The broader workflow is in keep your README in sync with code.

Honest limit

The embed URL exists only while the project is shared public. Make it private and the README image 404s. For confidential systems, a committed export regenerated in CI is the better fit, staleness accepted between runs.

FAQ

How do I embed an architecture diagram in a GitHub README?

Use a plain markdown image pointing at a URL that serves the current diagram, for example ![Architecture](https://datadef.io/api/embed/your-slug). GitHub strips iframes and scripts from READMEs, so an image by URL is the only embed form that can update without a commit. For small flows, a native Mermaid fenced block is the in-repo alternative.

Why does my README image not update after I changed the diagram?

GitHub proxies external images through camo.githubusercontent.com, which caches by URL and honors the origin cache headers. Datadef serves its embeds with a five minute cache lifetime, so updates normally appear within minutes. If a copy seems stuck, send an HTTP PURGE request to the camo URL visible in the rendered page and reload.

Can I embed an iframe in a GitHub README?

No. GitHub sanitizes README HTML and removes iframes, scripts, and styles entirely. The interactive form of a diagram cannot render in a README; use an image URL there and link out to the interactive page for readers who want to pan and zoom.

Does a live diagram embed work in a private repository README?

Yes, with a distinction worth naming. The repository can be private; the README is visible to whoever can see the repo, and the image loads normally. What must be public is the diagram itself: the embed URL only exists for a project shared public. A private repo showing a public diagram is a common and reasonable setup, but confidential architecture content should not go in the diagram.

Should I use Mermaid or an image URL for a README architecture diagram?

Use a Mermaid fenced block when the diagram is small, roughly under 20 nodes, and benefits from living in the same commit as the prose. Use an image URL when the diagram needs deliberate layout, zones, or column-level detail, or when it must stay current without anyone editing the README.

How do I show a dark mode diagram in a GitHub README?

GitHub supports the picture element in READMEs: add a source tag with media="(prefers-color-scheme: dark)" pointing at the dark asset and an img fallback for light mode. Readers get the variant matching their theme. A single image with its own solid background also reads acceptably on both themes if you prefer one URL.