HTML figure and figcaption

The <figure> element groups a piece of self-contained content — like an image, diagram, code listing, or chart — together with an optional caption provided by <figcaption>. Instead of just dropping an <img> on the page with a caption floating nearby in a plain paragraph, <figure> tells the browser and assistive technologies that this image and this caption belong together as one referenceable unit. This matters for accessibility, for SEO, and for keeping your markup meaningful rather than purely visual.

Overview / How it works

According to the HTML specification, <figure> represents content that is self-contained — meaning it could be moved away from the main flow of the document (to an appendix, a sidebar, or a different page) without affecting the meaning of the surrounding text. Think of how a figure works in a printed textbook: the body text might say “see Figure 3”, and Figure 3 itself — with its own caption — could be moved to another page of the book and the reader could still find and understand it. That’s exactly the relationship <figure> models in HTML.

<figcaption> is the caption for that content. It is optional, and when present it must be either the first or the last child of <figure> — you cannot put content before and after it, and you cannot put more than one <figcaption> inside a single <figure>.

In terms of the DOM and default rendering, both elements are treated as block-level by default in the browser’s built-in style sheet. <figure> typically renders with a small default margin on the left and right (historically 40px in most browsers, inherited from the old <center>-adjacent conventions), and <figcaption> renders as an ordinary block with no special styling — it looks like a paragraph unless you style it with CSS. Neither element has any default border, background, or numbering; any visual “framing” you see around figures on real websites comes entirely from CSS, not from the browser’s defaults. Semantically, both elements belong to flow content, and <figure> is also sectioning content-adjacent in the sense that it can contain headings, paragraphs, images, tables, code blocks — essentially any flow content — alongside its single optional <figcaption>.

A common misconception is that <figure> must contain an image. It does not. A figure can wrap a <table>, a code sample, a blockquote, a chart, or even several images treated as one unit (a photo gallery item, for example). The defining trait is not “it’s a picture” — it’s “this is a self-contained, referenceable chunk of content with an optional caption.”

Syntax

<figure>
  <img src="chart.png" alt="Bar chart of quarterly sales">
  <figcaption>Figure 1: Quarterly sales by region</figcaption>
</figure>
Part Description
<figure> The container element. Accepts global attributes (id, class, etc.) but has no attributes of its own.
<figcaption> An optional caption. Must be the first or last child of <figure> if present; at most one per figure.
content Any flow content: images, tables, <pre> code blocks, blockquotes, even multiple images grouped together.

Examples

Example 1: A simple captioned image

<figure>
  <img src="mount-fuji.jpg" alt="Mount Fuji at sunrise" width="400" height="267">
  <figcaption>Mount Fuji at sunrise, seen from Lake Kawaguchi.</figcaption>
</figure>

Result: The browser renders the image at 400 by 267 pixels, with a line of caption text directly beneath it reading “Mount Fuji at sunrise, seen from Lake Kawaguchi.” The whole block sits with a small default left/right margin, visually setting it apart from surrounding paragraphs, though no border or background appears without CSS.

This is the most common use case: pairing an image with a description of what it shows. The alt attribute still does its own job — describing the image for screen readers and when the image fails to load — while <figcaption> provides a caption that’s visible to everyone, sighted or not, and programmatically associated with the figure.

Example 2: Captioning a code sample

<figure>
  <pre><code>function add(a, b) {
  return a + b;
}</code></pre>
  <figcaption>Listing 1: A basic addition function in JavaScript.</figcaption>
</figure>

Result: The browser displays the code in its default monospace, whitespace-preserving <pre> block, followed immediately by the caption text “Listing 1: A basic addition function in JavaScript.” below it.

This shows that figures are not limited to images. Documentation sites and tutorials frequently wrap code listings in <figure> so each snippet can carry its own labeled caption, just like a numbered listing in a textbook.

Example 3: Grouping multiple images in one figure

<figure>
  <img src="cat-1.jpg" alt="A tabby cat sleeping on a windowsill" width="200" height="150">
  <img src="cat-2.jpg" alt="The same cat stretching" width="200" height="150">
  <figcaption>Our cat Whiskers, before and after her afternoon nap.</figcaption>
</figure>

Result: Two images render side by side (or stacked, depending on available width, since <img> is inline by default), followed by a single shared caption underneath describing both.

Because <figure> can hold any amount of flow content before its <figcaption>, it’s valid — and useful — to group several related images under one caption rather than writing a separate <figure> for each.

How it works step by step / Under the hood

When the browser’s HTML parser encounters <figure>, it creates a generic block-level box in the DOM, just like it would for a <div>, but tags the element’s semantic role as “figure” in the accessibility tree. This is the key difference from a plain <div>: assistive technologies expose <figure> with an ARIA role of figure automatically, and when a <figcaption> is present, most browsers use it to compute the figure’s accessible name — meaning a screen reader can announce “Mount Fuji at sunrise, seen from Lake Kawaguchi, figure” as a single unit, rather than reading an image and an unrelated paragraph as two disconnected things.

Because the association between a figure and its caption is structural (parent/child in the DOM) rather than purely visual (CSS positioning), it survives across contexts where CSS doesn’t apply — printed pages, reader-mode views, screen readers, and text-only browsers all still know which caption belongs to which figure.

Common Mistakes

Mistake 1: Putting figcaption outside figure

<figure>
  <img src="owl.jpg" alt="A barn owl in flight">
</figure>
<figcaption>A barn owl in flight at dusk.</figcaption>

This is structurally valid HTML — the browser won’t error — but it defeats the whole purpose of the feature. Once <figcaption> is outside its <figure>, it is just a stray element with no special relationship to the image; assistive technology can no longer treat it as the figure’s caption. The fix is to move it inside:

<figure>
  <img src="owl.jpg" alt="A barn owl in flight">
  <figcaption>A barn owl in flight at dusk.</figcaption>
</figure>

Mistake 2: Using two figcaptions, or one in the middle

<figure>
  <figcaption>Top caption</figcaption>
  <img src="map.png" alt="City map">
  <figcaption>Bottom caption</figcaption>
</figure>

The HTML specification only allows a single <figcaption> per <figure>, and it must be the first child or the last child — never both at once. A validator will flag this as invalid content model usage. Pick one caption, in one position:

<figure>
  <img src="map.png" alt="City map">
  <figcaption>City map showing downtown transit lines.</figcaption>
</figure>

Mistake 3: Using figure as a generic styling wrapper

Some developers reach for <figure> any time they want a box with a margin around an image, even when there’s no caption and no self-contained content — for example, wrapping a decorative background image or a logo that’s part of the page chrome. If the content isn’t actually a discrete, referenceable, self-contained item, a plain <div> (styled with CSS) is the more accurate choice. Reserve <figure> for genuine illustrative content: photos, diagrams, charts, code listings, quotations.

Best Practices

  • Use <figcaption> to describe what the figure shows or means (context, source, or explanation) — reserve the alt attribute on <img> for a concise description of the image content itself; the two serve different, complementary purposes.
  • Only wrap content in <figure> when it is truly self-contained — content that could be moved to an appendix or sidebar without breaking the surrounding text’s meaning.
  • Keep captions concise and factual; avoid duplicating the exact wording of the alt text verbatim.
  • Remember <figure> isn’t just for images — use it for code listings, tables, blockquotes, and charts too.
  • Don’t use <figure> purely as a CSS hook for spacing or layout when there’s no caption or self-contained meaning involved; use <div> for pure presentation grouping instead.
  • Style the visual appearance (borders, background, spacing) with CSS in a separate stylesheet rather than relying on default browser margins.

Practice Exercises

  • Create a <figure> containing an image of your choice with a meaningful alt attribute, and add a <figcaption> that explains the context of the image (for example, where and when it was taken).
  • Build a figure that wraps a short <pre><code> snippet showing an HTML list, and caption it “Listing 1: An unordered list example.”
  • Take a figure with two images side by side and one shared caption, then rewrite it as two separate figures, each with its own caption. Compare how the meaning changes for a screen reader user.

Summary

  • <figure> groups self-contained content — images, code, tables, quotes — that could be moved elsewhere in the document without breaking the surrounding text’s meaning.
  • <figcaption> provides an optional caption and must be the first or last child of its <figure>, with at most one per figure.
  • Both render as block-level elements by default, with no special visual styling beyond a small default margin on <figure>.
  • The parent/child structural relationship lets assistive technology and the accessibility tree associate a caption with its figure automatically, unlike a caption written as a nearby but unrelated paragraph.
  • <figure> is not exclusively for images — it works equally well for code listings, tables, and grouped image sets.