feat(mdx): add mermaid diagram support - #9107
Conversation
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
There was a problem hiding this comment.
Pull request overview
This PR adds Mermaid diagram rendering support to the site’s MDX pipeline by transforming ```mermaid fenced blocks into a renderable form during MDX compilation and then rendering them client-side at runtime.
Changes:
- Added
rehype-mermaidto the MDX rehype plugin chain (before Shiki) and addedmermaid/rehype-mermaiddependencies. - Introduced a new
MermaidMDX component that lazy-loads Mermaid on the client and re-renders on theme changes. - Updated the
MDXCodeBox(preoverride) to route Mermaid blocks to the new Mermaid renderer instead of the standard code box.
Reviewed changes
Copilot reviewed 5 out of 6 changed files in this pull request and generated 1 comment.
Show a summary per file
| File | Description |
|---|---|
| pnpm-lock.yaml | Locks new dependencies pulled in by mermaid and rehype-mermaid. |
| apps/site/package.json | Adds mermaid and rehype-mermaid runtime dependencies. |
| apps/site/mdx/plugins.mjs | Inserts rehype-mermaid before Shiki to avoid Mermaid blocks being highlighted as plain code. |
| apps/site/components/MDX/Mermaid/index.tsx | New client component that loads Mermaid lazily and renders diagrams (with theme support). |
| apps/site/components/MDX/Mermaid/index.module.css | Basic layout constraints for Mermaid-rendered SVG output. |
| apps/site/components/MDX/CodeBox/index.tsx | Detects Mermaid <pre class="mermaid"> blocks and renders the Mermaid component instead of CodeBox. |
Files not reviewed (1)
- pnpm-lock.yaml: Generated file
💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.
| const renderDiagram = async () => { | ||
| // Mermaid is heavy, so we only load it on the client when needed | ||
| const { default: mermaid } = await import('mermaid'); | ||
|
|
||
| mermaid.initialize({ | ||
| startOnLoad: false, | ||
| theme: resolvedTheme === 'dark' ? 'dark' : 'default', | ||
| }); | ||
|
|
||
| try { | ||
| const { svg } = await mermaid.render( | ||
| `mermaid-${reactId}`, | ||
| String(children).trim() | ||
| ); | ||
|
|
||
| if (!cancelled && containerRef.current) { | ||
| containerRef.current.innerHTML = svg; | ||
| } | ||
| } catch { | ||
| // If the diagram source is invalid, fall back to showing the source | ||
| if (!cancelled && containerRef.current) { | ||
| containerRef.current.textContent = String(children); | ||
| } | ||
| } | ||
| }; |
Adds rehype-mermaid (pre-mermaid strategy) to the MDX rehype chain before Shiki, and renders the resulting pre.mermaid blocks with a client-side Mermaid component that follows the site's theme. Fixes: nodejs#7540
4b6eca2 to
bc37755
Compare
|
Addressed the Copilot review on the Mermaid component (head
|
| import rehypeShikiji from '@node-core/rehype-shiki/plugin'; | ||
| import remarkHeadings from '@vcarl/remark-headings'; | ||
| import rehypeAutolinkHeadings from 'rehype-autolink-headings'; | ||
| import rehypeMermaid from 'rehype-mermaid'; |
There was a problem hiding this comment.
Doesn't this dramatically slow down builds since it requires initializing a whole browser instance?
There was a problem hiding this comment.
No — with strategy: 'pre-mermaid' no browser is ever started at build time. Verified in the dependency source:
createMermaidRenderer()only creates a lazybrowserPromise(browserPromise ||= getBrowser(...)inside the returned render function inmermaid-isomorphic) — nothing launches at plugin setup.rehype-mermaidnever calls that render function forpre-mermaid; it only rewrites the AST (<pre><code class="language-mermaid">→<pre class="mermaid">). The package comments this exact path as "No need to start a browser in this case."- Playwright (peer dep) is only exercised by the
inline-svg/img-*strategies.
So build cost is a single AST walk per document, and rendering happens client-side — with the mermaid library itself lazy-loaded via dynamic import(), so it stays out of the initial bundle too.
Happy to switch to inline-svg if the team prefers zero client-side JS — that's the trade-off (build-time Chromium vs. client rendering).
Adds Mermaid diagram support to the MDX pipeline, as requested in #7540 (blessed there by @AugustinMauroy, with
rehype-mermaidsuggested by @flakey5).How it works
rehype-mermaid(strategypre-mermaid) is added to the rehype chain inapps/site/mdx/plugins.mjs, positioned before@node-core/rehype-shikiso```mermaidfenced blocks become<pre class="mermaid">instead of being syntax-highlighted as plain code.MDXCodeBox(thepreMDX override) detects themermaidclass and renders a newMermaidclient component instead of a code box.Mermaidcomponent lazy-loads themermaidlibrary on the client (keeping it out of the initial bundle) and re-renders on theme change vianext-themes(default/darkthemes).Testing
```mermaidblocks emit<pre class="mermaid">with the diagram source intact, while regular code blocks still go through Shiki highlighting.eslintandstylelintpass on all changed files.Happy to switch to a build-time strategy (
inline-svg) if preferred — I chose client-side rendering to avoid adding a heavy build-time dependency (Playwright/Chromium).Fixes #7540