Docs & Code Guide

Embed a diagram in Hugo: raw HTML needs the unsafe flag, the figure shortcode does not

Hugo is the one static site generator where a pasted iframe disappears with a trace: goldmark, its markdown renderer, strips raw HTML by default and leaves an HTML comment reading raw HTML omitted where your embed used to be. There are two clean ways out, a one-line config flag and a built-in shortcode, and knowing both keeps the choice deliberate.

6 min readFor teams running docs or engineering blogs on Hugo

See it as a diagram

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

156/20003 credits left
Try:

No account needed · Editable canvas, not a picture

Why your iframe vanished: goldmark

Since Hugo moved to goldmark as its markdown renderer, inline HTML in content files is dropped unless you opt in, and the tell is unmistakable: view source on the built page and find <!-- raw HTML omitted --> where the iframe or img tag was. Nothing errored; the renderer did what its default told it to.

The opt-in is one line in your site config. With unsafe = true, raw HTML in markdown passes through, and an iframe pointing at an interactive diagram page renders with pan and zoom. The name overstates the danger for a docs site whose authors are your own team, but it is a site-wide switch, which is why the shortcode route below exists.

# hugo.toml
[markup.goldmark.renderer]
  unsafe = true

The figure shortcode needs no config at all

Hugo ships a figure shortcode that renders an image with optional caption, link, and sizing, and it takes an external src without any goldmark change, because shortcodes are templates, not markdown HTML. For the common case, a live diagram with a caption in a docs page, this is the cleanest route: no site-wide flag, theme-consistent output.

Plain markdown image syntax also works untouched by the unsafe setting, since it is markdown, not raw HTML: ![Architecture](https://datadef.io/api/embed/my-platform-a1b2c3d4) renders on a stock config.

{{< figure
  src="https://datadef.io/api/embed/my-platform-a1b2c3d4"
  alt="Platform architecture"
  caption="Current platform architecture, rendered from the live diagram."
>}}

Render hooks, if you want every image treated the same

Hugo's image render hooks let a template intercept every markdown image: create layouts/_default/_markup/render-image.html and the site decides how images render in one place. Teams use this to wrap diagrams in figure markup, add loading="lazy", or route images matching a URL pattern into a styled container, all without touching content files.

A hook is also the tidy way to get iframe-like presentation without unsafe: markdown stays plain image syntax, and the hook emits whatever HTML your design needs around it.

Hugo's asset pipeline ignores external URLs, usefully

Hugo Pipes and image processing operate on page resources and files in assets/, local files. An external URL in markdown, a shortcode, or a render hook is emitted into the HTML verbatim: nothing is downloaded at build time, nothing fingerprinted, nothing frozen. The deployed site therefore shows the diagram as it is at view time, and with a five minute cache lifetime on the image, an edit reaches published pages within minutes with no rebuild.

This is the property that a committed PNG never has, and skipping the export-commit-deploy cycle is what keeps the diagram from rotting alongside the post that embedded it, the failure mode described in README rot.

Keeping the diagram current, and the limits

The embed keeps Hugo pages faithful to the diagram. Keeping the diagram faithful to the system is the loop Datadef closes with its MCP server (registry name io.datadef/mcp): an agent with repository access updates the diagram from what it reads in the code, invoked after a merge, in CI, or on request. Datadef does not watch repositories; nothing regenerates until asked. A paid-plan API key lets the agent in.

The limit to plan around: embed URLs exist only while the project is shared public, and 404 if it goes private. For a public docs site that is usually fine; keep confidential internals out of the shared diagram regardless.

FAQ

Why does my iframe not render in Hugo?

Hugo's markdown renderer, goldmark, strips raw HTML by default and leaves a <!-- raw HTML omitted --> comment in the output. Enable pass-through with [markup.goldmark.renderer] unsafe = true in hugo.toml, or avoid raw HTML entirely with the figure shortcode or plain markdown image syntax.

How do I embed an external image in Hugo without the unsafe flag?

Two ways that need no config: plain markdown syntax ![alt](https://your-url), which is markdown rather than raw HTML, or the built-in figure shortcode with src set to the URL, which also gives you a caption and sizing options.

Is markup.goldmark.renderer.unsafe dangerous?

It allows raw HTML from your content files into the output, so the risk is bounded by who writes your content. For a docs site authored by your own team it is a common and reasonable setting; for sites rendering untrusted contributions, prefer shortcodes and render hooks instead.

Does Hugo download external images at build time?

No. Hugo's asset pipeline only processes local files; external URLs are emitted into the HTML verbatim. Readers fetch the image at view time, which means a live diagram endpoint updates on the published site within minutes of an edit, no rebuild required.

Can I style all diagram embeds in one place in Hugo?

Yes, with an image render hook: layouts/_default/_markup/render-image.html intercepts every markdown image, so you can wrap diagrams in figure markup, add lazy loading, or apply a styled container site-wide while content files keep plain markdown syntax.