A single goldmark extension that bundles HTML-attribute support for headings,
block elements, and images — plus an <img...> to <figure> renderer with options.
It composes four capabilities behind one goldmark.Extender:
- Heading attributes (goldmark built-in
parser.WithAttribute):→## Section {#intro .lead}<h2 id="intro" class="lead">Section</h2>
Goldmark auto-ids are over-written if an explicit {#your-new-id} id attribute is provided.
-
Block attributes for lists, tables, blockquotes, paragraphs, etc. Put the attribute list on the line directly below the block:
- Apple - Orange - Banana {.fruits}
→
<ul class="fruits">…</ul> -
Standalone images are not wrapped in
<p>, so an attribute list on the following line attaches to the image rather than the paragraph. -
<figure>rendering for standalone (not in-line) images, with optional baked-in class andloading="lazy" decoding="async":
→
<figure class="photo"> <img src="/img/river.jpg" alt="A river at dusk" loading="lazy" decoding="async"> </figure>
Inline images (with text on the same line) stay as a plain <img> and do not
receive loading="lazy"/decoding="async" — those apply to standalone images
only, since an inline icon is typically above the fold.
To set a class (or other attributes) on a specific figure, put an attribute list on the line directly below the image:

{.wide #hero}→
<figure id="hero" class="wide">
<img src="/img/river.jpg" alt="A river at dusk" loading="lazy" decoding="async">
</figure>A harvested class is appended to any WithFigureClass default.
Captions. A <figcaption> is emitted only when you supply an explicit
caption="..." in the attribute list. Alt text is never used as a caption:
alt describes the image (for assistive tech), while a caption is editorial,
and the two are intentionally independent.

{.wide #hero caption="The Peace River at dusk, April 2026"}→
<figure id="hero" class="wide">
<img src="/img/river.jpg" alt="Long-exposure shot of the river" loading="lazy" decoding="async">
<figcaption>The Peace River at dusk, April 2026</figcaption>
</figure>To control figure wrapping per image, use the reserved figure directive in
the attribute list. It is absolute — it overrides the global default either
way:

{figure=false}renders a bare <img> even though figures are the default, and

{figure=true}forces a <figure> even when figures are globally disabled via
WithoutFigures(). When figure=false, any other attributes in the list
(class, id, …) fall through onto the <img> instead of a <figure>; a
caption in that case is dropped, since a bare <img> has no caption.
import (
"bytes"
"github.com/mwyvr/goldmark-html5"
"github.com/yuin/goldmark"
"github.com/yuin/goldmark/extension"
"github.com/yuin/goldmark/parser"
)
md := goldmark.New(
goldmark.WithExtensions(
extension.GFM,
gmhtml5.New(
gmhtml5.WithFigureClass("photo"),
),
),
goldmark.WithParserOptions(
parser.WithAutoHeadingID(),
),
)
var buf bytes.Buffer
_ = md.Convert(src, &buf)| Option | Effect |
|---|---|
WithFigureClass("x") |
adds class="x" to every <figure> |
WithoutFigures() |
standalone images render as bare un-wrapped <img> (attribute lists still attach) |
WithoutLazyImages() |
drop loading="lazy" decoding="async" from standalone images (inline images never get them) |
WithoutBlockAttributes() |
disable block attribute lists (headings still supported) |
The block transformer and image/paragraph renderers drop on* event-handler
attributes defensively, mirroring Hugo's content-security behavior. This is
not a full sanitizer. If you render untrusted markdown, also run the output
through a sanitizer such as github.com/microcosm-cc/bluemonday, and leave
goldmark's html.WithUnsafe() off (it is off by default).
The block-attribute support is derived from Hugo's goldmark attributes extension (Apache 2.0, © The Hugo Authors), which is itself based on goldmark-attributes (MIT, © 2019 Dmitry Sedykh). Built on goldmark (MIT, © 2019 Yusuke Inuzuka). See NOTICE for full attribution and license texts.