nextra-dgmo
v0.3.0
Published
Nextra integration to render DGMO diagrams from fenced code blocks at build time. Two-step install: `withDgmo()` in next.config.mjs plus `<DgmoClient />` in app/layout.tsx — the client component handles route-change rebinding and auto-imports the theme-aw
Maintainers
Readme
nextra-dgmo
Render DGMO diagrams from ```dgmo fenced code blocks in your Nextra site at build time. Powered by @diagrammo/dgmo and the framework-agnostic remark-dgmo core. Zero client JavaScript by default (one tiny re-binder fires on route change).
📖 Setup guide: diagrammo.app/embed#nextra · 🔭 Live showcase: every chart type rendered through nextra-dgmo — every block is in showcase mode, so hovering a diagram reveals its copy / open-in-editor footer.
Every diagram is rendered twice at build time (light + dark palettes) and follows Nextra's color-mode toggle through a shipped, .dark-rewritten stylesheet.
Chart types & visual authoring
One small plain-text language, 45 chart types — flowcharts, sequence, state, class, ER, C4, org charts, gantt, maps, mind maps, and the full bar/line/pie/area/sankey family. Browse every type with live examples in the language reference.
Prefer to author visually? Draft diagrams in the Diagrammo desktop app or the online editor — live preview, autocomplete, optional vim keybindings, 7 themeable palettes, and one-click PNG/SVG export — then paste the dgmo block into your docs. More at diagrammo.app.
Install
pnpm add nextra-dgmo @diagrammo/dgmo@diagrammo/dgmo, nextra, next, and react are peer dependencies. Node 20.6+.
⚠️ Turbopack caveat (read first)
Nextra wires DGMO in through next.config.mjs, and withDgmo injects the remark-dgmo plugin as a function-valued MDX option. Turbopack cannot serialize function-valued remark plugins, so:
- Production builds: use Webpack —
next build(Nextra 4's default). Do not pass--turbopacktonext build. - Dev: run
next devwithout--turbopack.
The build/SSG path that actually emits the inline SVG is unaffected — this is purely about how the plugin is passed to the compiler, and Webpack handles it fine. If your package.json scripts were scaffolded with --turbopack, drop that flag.
Quick start
Two small edits to your Nextra site.
1. next.config.mjs — wire the remark plugin
import nextra from 'nextra';
import { withDgmo } from 'nextra-dgmo/config';
const withNextra = nextra(
withDgmo(
{/* your existing nextra options, if any */},
{ dgmo: { palette: 'slate' } }
)
);
export default withNextra({/* your next config */});withDgmo() augments your Nextra config's mdxOptions.remarkPlugins with remark-dgmo and defaults mdx: true on it so blocks render as MDX-safe mdxJsxFlowElement nodes. Idempotent. Unlike Fumadocs, Nextra nests the remark pipeline under mdxOptions, so withDgmo injects one level deeper — you don't have to know that; just pass your Nextra options object through it. If you already have your own remark plugins:
const withNextra = nextra(
withDgmo({ mdxOptions: { remarkPlugins: [myOtherPlugin] } })
);remark-dgmo is prepended so it runs before any downstream remark plugin.
2. app/layout.tsx — mount the client component
import './global.css';
import { DgmoClient } from 'nextra-dgmo/client';
import type { ReactNode } from 'react';
export default function RootLayout({ children }: { children: ReactNode }) {
return (
<html lang="en" suppressHydrationWarning>
<body>
{children}
<DgmoClient />
</body>
</html>
);
}<DgmoClient /> is a no-render Client Component that does two things:
- Runs
bindDgmo()on initial mount and on every soft route change — Next's app router doesn't refireDOMContentLoadedsemantics on client-side navigation, so without this you'd lose viewBox tightening and showcase-mode copy buttons after the first SPA transition. - Side-effect-imports the shipped
nextra-dgmo/client.cssso Next's CSS pipeline picks it up automatically. The stylesheet is generated fromremark-dgmo/client.cssat build time with[data-theme="dark"]rewritten tohtml.dark— the attribute Nextra'snext-themesdefault uses. No manual@importrequired.
That's the whole integration.
Passing remark-dgmo options
const withNextra = nextra(
withDgmo({}, { dgmo: { palette: 'catppuccin', colorMode: 'auto' } })
);The second argument forwards anything in remark-dgmo's option surface (palette, theme, colorMode, mode, className, etc.).
Options
The second argument to withDgmo is { dgmo: DgmoOptions }. Common keys:
| Option | Type | Default | Effect |
| ----------- | ------------------------------- | -------- | ----------------------------------------------------------------------------- |
| palette | palette name | slate | Which of the 7 built-in palettes to render with. |
| theme | theme name | — | Named theme preset (overrides individual palette/colorMode where it applies). |
| colorMode | 'auto' \| 'light' \| 'dark' | 'auto' | auto dual-renders light + dark and toggles via the host's .dark class. |
| mode | render mode | — | Passthrough to @diagrammo/dgmo's render mode. |
| showcase | boolean (per-block info string) | false | Renders the source alongside the diagram with a copy-to-clipboard button. |
Per-block overrides live on the fence info string (see below); the withDgmo dgmo option sets the site-wide defaults.
Configure (manual)
If withDgmo doesn't fit (dynamic remark plugin loading, you need to inspect the wiring), do it by hand — remember Nextra's remark slot lives under mdxOptions:
import nextra from 'nextra';
import remarkDgmo from 'nextra-dgmo/remark';
const withNextra = nextra({
mdxOptions: {
remarkPlugins: [[remarkDgmo, { mdx: true }]],
},
});
export default withNextra({});The mdx: true option is required — Nextra always routes content through @mdx-js/mdx, which rejects the raw html mdast nodes remark-dgmo emits by default.
Use
Drop a fenced block with the language dgmo into any .md/.mdx file in your Nextra content tree.
```dgmo
sequence
Client -POST /login-> API
API -validate-> Auth
Auth -JWT-> API
API -200 OK-> Client
```Per-block overrides
Append options to the fence info string. Tokens are space-separated; values may be quoted.
```dgmo showcase title="Login flow" palette=catppuccin colorMode=light
sequence
A -> B
```See the remark-dgmo README for the full option matrix.
How CSS is delivered
nextra-dgmo/client.css is the shipped stylesheet. It's the same three visibility rules + sizing + showcase chrome as upstream remark-dgmo/client.css, but the dark-mode selector is rewritten from [data-theme="dark"] to html.dark — the attribute Nextra's next-themes integration uses by default.
It's auto-imported by <DgmoClient /> via a side-effect import 'nextra-dgmo/client.css'. Next's CSS pipeline picks the import up from the Client Component module and extracts it into the page bundle. If you prefer to manage the import yourself, drop in the manual config path above and @import 'nextra-dgmo/client.css' from your app/global.css instead.
Custom color-mode selector
If you've configured next-themes with a non-default attribute (e.g. attribute="data-theme"), the shipped CSS won't match. Two options:
- Switch back to the default (Nextra's preset expects
html.dark). - Skip
<DgmoClient />'s auto-import and instead@import 'remark-dgmo/client.css'directly inapp/global.css(keys on[data-theme="dark"]), then override your theme's dark-mode rules to match.
See the "Custom color-mode selector" section in the remark-dgmo README for the underlying selectors.
How it works
withDgmoprependsremark-dgmo(withmdx: true) to your Nextra config'smdxOptions.remarkPlugins. Nextra's MDX build pipeline runs the plugin during compilation.- For each fenced
dgmoblock,remark-dgmocallsrender()from@diagrammo/dgmoonce per theme (under defaultcolorMode: 'auto') and replaces the block with anmdxJsxFlowElementcarrying the rendered SVG wrappers viadangerouslySetInnerHTML. - The shipped CSS, side-effect-imported by
<DgmoClient />, gates the wrappers' visibility onhtml.dark. Toggling Nextra's theme switcher flips the class, which flips visibility. - The
<DgmoClient />Client Component subscribes tousePathname()and re-runsbindDgmo()on every soft navigation. The function tightens each diagram'sviewBoxto content bounds and wires showcase-mode copy buttons.
All rendering happens at build time. The browser ships only inline SVG + the small CSS rules + a ~1.5 KB bindDgmo payload. This wrapper reuses the remark-dgmo core shared with the Astro, Docusaurus, and Fumadocs integrations.
License
MIT
