docusaurus-plugin-figure-caption
v1.0.0
Published
A remark plugin that auto-wraps images in <figure> with numbered <figcaption> and cross-reference support. Works with Docusaurus, MDX, and any unified/remark pipeline.
Maintainers
Readme
docusaurus-plugin-figure-caption
A remark plugin that auto-wraps images in <figure> elements with numbered <figcaption> captions and cross-reference support.
Works with Docusaurus 3+, MDX, and any unified/remark pipeline.
Features
- Auto-numbering — Images get
Figure 1:,Figure 2:, etc. automatically - Tags & cross-references — Tag an image with
#my-tag, then reference it anywhere withfig:my-tag - Cross-references resolve to clickable links —
fig:my-tagbecomes<a href="#fig-my-tag">Figure 3</a> - Docusaurus-native — Handles Docusaurus's JSX image transforms out of the box
- Smart exclusion — Skips images inside tables and links (badges, buttons)
- Configurable — Caption source, numbering mode, position, CSS classes, ID generation
- Dark mode ready — Included CSS supports Docusaurus dark theme
- Dual format — Ships ESM + CJS with full TypeScript declarations
Installation
npm install docusaurus-plugin-figure-caption
# or
pnpm add docusaurus-plugin-figure-caption
# or
yarn add docusaurus-plugin-figure-captionQuick Start
Docusaurus
// docusaurus.config.ts
import type { Config } from '@docusaurus/types';
const config: Config = {
// ...
presets: [
[
'classic',
{
docs: {
remarkPlugins: [
[require('docusaurus-plugin-figure-caption').default, { /* options */ }],
],
},
blog: {
remarkPlugins: [
[require('docusaurus-plugin-figure-caption').default, { /* options */ }],
],
},
},
],
],
};Import the default styles in your docusaurus.config.ts:
const config: Config = {
// ...
themes: {
customCss: [
'./src/css/custom.css',
'docusaurus-plugin-figure-caption/styles',
],
},
};Or import in your CSS:
@import 'docusaurus-plugin-figure-caption/styles';Generic remark / unified
import { remark } from 'remark';
import remarkHtml from 'remark-html';
import figureCaption from 'docusaurus-plugin-figure-caption';
const result = await remark()
.use(figureCaption, { numbering: 'per-page' })
.use(remarkHtml)
.process('');
console.log(String(result));
// <figure class="figure-container" id="fig-1">
// <img src="/img/sunset.jpg" alt="A beautiful sunset">
// <figcaption class="figure-caption">Figure 1: A beautiful sunset</figcaption>
// </figure>Usage
Basic — Auto-numbered captions
Every standalone image gets wrapped in a <figure> with a numbered caption:

Produces:
- Figure 1: Wind turbine at sunset
- Figure 2: Solar panel array
Tags — Label your figures
Add #tag-name at the end of the alt text to assign a referenceable label:
The #perf-chart tag is stripped from the visible caption. The figure gets id="fig-perf-chart".
Cross-references — Link to tagged figures
Write fig:tag-name anywhere in your text. It auto-resolves to a clickable link:

As shown in fig:architecture, the system uses a microservices pattern.
The detailed data flow (fig:architecture) confirms this approach.Renders as:
As shown in Figure 1, the system uses a microservices pattern. The detailed data flow (Figure 1) confirms this approach.
Images that are skipped
The plugin automatically skips:
- Images inside tables — Table cells often contain small icons
- Images inside links — Badge images, clickable buttons
- Images matching
excludepatterns — Custom exclusion rules
<!-- This image is NOT wrapped (it's in a link) -->
[](https://example.com)
<!-- This image IS wrapped -->
Configuration
All options are optional. Pass them as the second argument:
remarkPlugins: [
[require('docusaurus-plugin-figure-caption').default, {
numbering: 'per-page',
prefix: 'Figure',
separator: ':',
captionSource: 'alt',
captionPosition: 'bottom',
className: 'figure-container',
captionClassName: 'figure-caption',
generateId: true,
idPrefix: 'fig',
exclude: [],
tagPattern: /\s#([a-zA-Z0-9_-]+)$/,
refPrefix: 'fig:',
}],
],Options Reference
| Option | Type | Default | Description |
|--------|------|---------|-------------|
| numbering | 'per-page' \| 'per-section' \| 'continuous' \| false | 'per-page' | Numbering mode. false disables numbering entirely. |
| prefix | string | 'Figure' | Text before the number (e.g., 'Fig.', 'Imagen'). |
| separator | string | ':' | Character between number and caption text. |
| captionSource | 'alt' \| 'title' \| 'both' | 'alt' | Where to read caption text from. 'both' joins them with —. |
| captionPosition | 'top' \| 'bottom' | 'bottom' | Position of the caption relative to the image. |
| className | string | 'figure-container' | CSS class for <figure>. |
| captionClassName | string | 'figure-caption' | CSS class for <figcaption>. |
| generateId | boolean | true | Whether to add id attributes to <figure> elements. |
| idPrefix | string | 'fig' | Prefix for generated IDs. 'fig' → id="fig-1". |
| exclude | RegExp[] | [] | URL patterns to skip (e.g., [/badge/, /\.svg$/]). |
| tagPattern | RegExp | /\s#([a-zA-Z0-9_-]+)$/ | Regex to extract tags from alt text. |
| refPrefix | string | 'fig:' | Prefix for cross-references in text. |
Localization Example
// Spanish captions: "Figura 1: ..."
{ prefix: 'Figura' }
// German captions: "Abbildung 1 – ..."
{ prefix: 'Abbildung', separator: ' –' }
// No numbering, just caption text
{ numbering: false }Styling
Default Styles
Import the included stylesheet for a clean default look:
@import 'docusaurus-plugin-figure-caption/styles';This provides:
- Centered figures with responsive images
- Subtle box shadow on images
- Italic caption text with proper spacing
- Dark mode support for Docusaurus themes
Custom Styles
Override the default classes or use your own:
.figure-container {
display: flex;
flex-direction: column;
align-items: center;
margin: 2rem auto;
}
.figure-container img {
max-width: 100%;
border-radius: 8px;
}
.figure-caption {
font-size: 0.85rem;
color: #666;
margin-top: 0.5rem;
}Or use entirely custom class names:
{ className: 'my-figure', captionClassName: 'my-caption' }TypeScript
The plugin exports all types for full IntelliSense:
import type {
FigureCaptionOptions,
ResolvedOptions,
NumberingMode,
CaptionSource,
CaptionPosition,
FigureRef,
} from 'docusaurus-plugin-figure-caption';For advanced use, resolveOptions is also exported to programmatically merge defaults:
import { resolveOptions } from 'docusaurus-plugin-figure-caption';
const config = resolveOptions({ prefix: 'Fig.', numbering: false });
// config is fully resolved with all defaults appliedHow It Works
Image detection — The plugin visits all nodes in the Markdown AST. It handles both standard MDAST
imagenodes and Docusaurus's JSX<img>elements (which Docusaurus creates when transforming local image paths through webpack).Figure wrapping — Each qualifying image is wrapped in a
<figure>element with a<figcaption>containing the numbered caption.Tag extraction — If the alt text ends with
#tag-name, the tag is extracted, removed from the visible caption, and stored in an internal registry mapping tags to figure numbers.Cross-reference resolution — A second pass scans the document for
fig:tag-namereferences. In MDX mode, these are parsed astextDirectivenodes. In pure remark mode, they're matched via regex on text nodes. Each reference is replaced with a clickable<a>link to the tagged figure.
Compatibility
| Environment | Version | Status | |-------------|---------|--------| | Docusaurus | 3.x | ✅ Fully supported | | MDX | 2.x / 3.x | ✅ Handles JSX image transforms | | remark | 15.x | ✅ Standard remark plugin | | unified | 11.x | ✅ Works in any unified pipeline | | Node.js | ≥ 18 | ✅ Required |
License
MIT © John Lopez
