Skip to Content
Fumadocs

Mermaid

Rendering diagrams in your docs

Setup

Fumadocs doesn't have a built-in Mermaid wrapper provided, we recommend using mermaid directly.

You can decide the Mermaid renderer to configure:

Official Renderer

npm install mermaid

Create the Mermaid component:

components/mdx/mermaid.tsx
'use client';

import { use, useId, useSyncExternalStore } from 'react';

export function Mermaid({ chart }: { chart: string }) {
  // `false` on the server and during hydration
  const isClient = useSyncExternalStore(
    () => () => {},
    () => true,
    () => false,
  );

  if (!isClient) return;
  return <MermaidContent chart={chart} />;
}

const cache = new Map<string, Promise<unknown>>();

function cachePromise<T>(key: string, setPromise: () => Promise<T>): Promise<T> {
  const cached = cache.get(key);
  if (cached) return cached as Promise<T>;

  const promise = setPromise();
  cache.set(key, promise);
  return promise;
}

function subscribeTheme(onChange: () => void) {
  const observer = new MutationObserver(onChange);
  observer.observe(document.documentElement, { attributeFilter: ['class'] });
  return () => observer.disconnect();
}

function MermaidContent({ chart }: { chart: string }) {
  const id = useId();
  const theme = useSyncExternalStore(subscribeTheme, () =>
    document.documentElement.classList.contains('dark') ? 'dark' : 'default',
  );
  const { default: mermaid } = use(cachePromise('mermaid', () => import('mermaid')));

  mermaid.initialize({
    startOnLoad: false,
    securityLevel: 'loose',
    fontFamily: 'inherit',
    themeCSS: 'margin: 1.5rem auto 0;',
    theme,
  });

  const { svg, bindFunctions } = use(
    cachePromise(`${chart}-${theme}`, () => {
      return mermaid.render(id, chart.replaceAll('\\n', '\n'));
    }),
  );

  return (
    <div
      ref={(container) => {
        if (container) bindFunctions?.(container);
      }}
      dangerouslySetInnerHTML={{ __html: svg }}
    />
  );
}

Pele

Pele is a lightweight Mermaid renderer that outputs themeable SVG.

npm install pele
components/mdx/mermaid.tsx
import { useId } from 'react';
import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock';
import { render } from 'pele';

export function Mermaid({ chart }: { chart: string }) {
  const id = useId();

  try {
    // also lay out the chart for phones, the container query shows the one that fits
    const wide = render(chart, { idPrefix: id });
    const narrow = render(chart, { idPrefix: `${id}narrow`, maxWidth: 320 });

    if (narrow.width >= wide.width) return <div dangerouslySetInnerHTML={{ __html: wide.svg }} />;

    return (
      <div className="@container">
        <div className="@max-[600px]:hidden" dangerouslySetInnerHTML={{ __html: wide.svg }} />
        <div className="@min-[600px]:hidden" dangerouslySetInnerHTML={{ __html: narrow.svg }} />
      </div>
    );
  } catch {
    return (
      <CodeBlock title="Mermaid">
        <Pre>{chart}</Pre>
      </CodeBlock>
    );
  }
}

Map its fonts and colors to Fumadocs, so diagrams follow light/dark mode:

global.css
.pele {
  --pele-font: var(--font-sans);
  --pele-font-mono: var(--font-mono);
  --pele-bg: var(--color-fd-background);
  --pele-fg: var(--color-fd-foreground);
  --pele-muted: var(--color-fd-muted-foreground);
  --pele-line: var(--color-fd-muted-foreground);
  --pele-surface: var(--color-fd-card);
  --pele-surface-alt: var(--color-fd-secondary);
  --pele-border: var(--color-fd-border);
  --pele-accent: var(--color-fd-primary);
}

Beautiful Mermaid

beautiful-mermaid is a 3rd party Mermaid renderer.

npm install beautiful-mermaid
components/mdx/mermaid.tsx
import { CodeBlock, Pre } from 'fumadocs-ui/components/codeblock';
import { renderMermaidSVG } from 'beautiful-mermaid';

export function Mermaid({ chart }: { chart: string }) {
  try {
    const svg = renderMermaidSVG(chart, {
      bg: 'var(--color-fd-background)',
      fg: 'var(--color-fd-foreground)',
      interactive: true,
      transparent: true,
    });

    return <div dangerouslySetInnerHTML={{ __html: svg }} />;
  } catch {
    return (
      <CodeBlock title="Mermaid">
        <Pre>{chart}</Pre>
      </CodeBlock>
    );
  }
}

Usage

Add the component as a MDX component:

components/mdx.tsx
import defaultMdxComponents from 'fumadocs-ui/mdx';
import { Mermaid } from '@/components/mdx/mermaid'; 
import type { MDXComponents } from 'mdx/types';

export function getMDXComponents(components?: MDXComponents) {
  return {
    ...defaultMdxComponents,
    Mermaid, 
    ...components,
  } satisfies MDXComponents;
}

Then, use it in MDX files.

<Mermaid
  chart="
graph TD;
subgraph AA [Consumers]
A[Mobile app];
B[Web app];
C[Node.js client];
end
subgraph BB [Services]
E[REST API];
F[GraphQL API];
G[SOAP API];
end
Z[GraphQL API];
A --> Z;
B --> Z;
C --> Z;
Z --> E;
Z --> F;
Z --> G;"
/>
ConsumersServicesServicesMobile appWeb appNode.js clientREST APIGraphQL APISOAP APIGraphQL API
ConsumersServicesServicesMobile appWeb appNode.js clientREST APIGraphQL APISOAP APIGraphQL API

As CodeBlock

You can convert mermaid codeblocks into the MDX usage with remarkMdxMermaid, a remark plugin.

source.config.ts
import { remarkMdxMermaid } from 'fumadocs-core/mdx-plugins';
import { defineConfig } from 'fumadocs-mdx/config';

export default defineConfig({
  mdxOptions: {
    remarkPlugins: [remarkMdxMermaid],
  },
});
```mermaid
graph TD;
  A-->B;
  A-->C;
```

How is this guide?

Last updated on