Mermaid Diagrams

Written and tested by the MD Converter editorial team

Mermaid Diagram Text Missing or Cut Off in SVG/PNG Exports: Causes and Fixes

When you export a Mermaid diagram to SVG or PNG, labels can disappear, get clipped, or the image shrinks. The causes are HTML labels inside foreignObject and web fonts that aren't available when the SVG is drawn as an image. Learn the right htmlLabels settings, font embedding, and sizing, with tested results and code.

When text disappears or gets clipped after exporting a Mermaid diagram to SVG or PNG, there are two main causes. First, Mermaid draws labels as HTML inside the SVG (foreignObject) by default. Apps other than browsers often can't draw that HTML, so only the text vanishes. Second, when an SVG is drawn as an image, it can't use the web fonts loaded on the page, so text rendered in a wider fallback font is cut off by its box.

Both are fixed with htmlLabels: false, which makes Mermaid draw labels as SVG text (<text>). Set it at the top level and under flowchart to be safe. If you need to keep a web font's look, embed the font in the SVG.

The results in this article were tested with Mermaid 11.4.0 and 11.16.1, Chrome, and macOS's built-in SVG conversion (sips) and Quick Look.

Quick diagnosis by symptom

SymptomMain causeFix
Text shows in the browser but disappears in other appsLabels are drawn as foreignObject (HTML)Set htmlLabels: false
The end of each label is cut off in the PNG (e.g., "Markdown docur")Web fonts aren't available when drawn as an image, so a wider fallback overflowshtmlLabels: false, or embed the font in the SVG
The PNG is smaller or blurrier than expectedwidth="100%" on the SVG leaves its image size undefinedSet width and height from the viewBox and scale up
Only edge labels (the "yes" in `-->yes`) disappear

Cause 1: labels are drawn as HTML (foreignObject)

By default, Mermaid flowcharts draw node and edge labels as HTML (<div>, <p>) inside <foreignObject>. foreignObject embeds HTML inside SVG; browsers can render HTML, so everything looks fine there.

<foreignObject width="72.19" height="24">
  <div xmlns="http://www.w3.org/1999/xhtml" style="display: table-cell; white-space: nowrap; ...">
    <span class="nodeLabel"><p>Markdown</p></span>
  </div>
</foreignObject>

Apps other than browsers often have no way to render HTML, and then the contents of foreignObject are skipped entirely. Converting with macOS's built-in SVG conversion (sips -s format png diagram.svg --out diagram.png) left empty boxes: the foreignObject labels were gone. The same diagram drawn with SVG <text> labels rendered correctly.

You can check whether an exported SVG contains foreignObject with a simple string search:

const { svg } = await mermaid.render("diagram-1", code);
console.log((svg.match(/<foreignObject/g) ?? []).length); // 0 means every label is <text>

Fix: set htmlLabels to false (top level and flowchart)

With htmlLabels: false, Mermaid draws labels as <text>. That's a basic SVG feature, so apps other than browsers can display it.

Where you put the setting matters, and the behavior differs between versions. Here's what happened with a diagram that has an edge label (A -->|yes| B):

SettingMermaid 11.4.0Mermaid 11.16.1
Top-level htmlLabels: false onlyNodes use <text>, edge labels stay foreignObjectEverything uses <text>
flowchart.htmlLabels: false onlyNo effectOnly edge labels use <text>
BothEverything uses <text>Everything uses <text>

Setting both works on either version:

// in your site-wide initialization
mermaid.initialize({
  startOnLoad: false,
  htmlLabels: false,
  flowchart: { htmlLabels: false },
});

To set it per diagram, add a directive at the top of the diagram:

%%{init: {"htmlLabels": false, "flowchart": {"htmlLabels": false}}}%%
flowchart LR
  A[Start] -->|yes| B[End]

With htmlLabels: false, features that rely on HTML labels, such as HTML-based formatting inside labels, may stop working. Text position and wrapping also change slightly, so review the exported result once.

Cause 2: web fonts aren't available when drawn as an image

The usual way to turn an SVG into a PNG is to load it as an Image and draw it on a canvas. An SVG loaded as an image, however, can't use web fonts loaded on the page, because images don't fetch external files.

Mermaid sizes each label box by measuring the text with the page's fonts. If the page uses a web font, boxes are sized for that font, but the PNG is drawn with a fallback font. If the fallback is wider, whatever overflows the foreignObject box is cut off.

We loaded a narrow web font (Roboto Condensed) on the page to test this. The diagram looked correct on the page, but in the PNG "Markdown document" was cut to "Markdown docur" and "Rendered preview" to "Rendered pre".

There are two fixes:

async function embedFont(svgRoot, family, fontUrl) {
  const bytes = new Uint8Array(await (await fetch(fontUrl)).arrayBuffer());
  let binary = "";
  for (const b of bytes) binary += String.fromCharCode(b);
  const style = svgRoot.ownerDocument.createElementNS("http://www.w3.org/2000/svg", "style");
  style.textContent =
    `@font-face{font-family:"${family}";src:url(data:font/woff2;base64,${btoa(binary)}) format("woff2");}`;
  svgRoot.insertBefore(style, svgRoot.firstChild);
}

The catch is character subsets. Services like Google Fonts serve one font as separate files per subset (Cyrillic, Latin, and so on). Embedding the first file we found didn't help: it had no Latin glyphs, and the text stayed clipped. Check unicode-range in the CSS and pick the file covering the characters in your diagram (for English text, the one including U+0000-00FF). CJK fonts are large, so specifying a font that viewers already have via fontFamily is usually more practical than embedding.

Pitfall: width="100%" leaves the image size undefined

Mermaid outputs SVGs whose root element has width="100%" and a max-width in style. That's handy for embedding in a page, but loaded as an Image the size is undefined, and Chrome treated it as the default 300 px wide. Building a canvas from img.naturalWidth then produces a small, blurry PNG.

The viewBox holds the diagram's real size, so set width and height from it before loading. For high-density screens or print, draw onto a canvas two or more times larger.

Example: PNG export that keeps the text

This function combines the fixes above. It's meant to run in a browser.

import mermaid from "mermaid";

mermaid.initialize({
  startOnLoad: false,
  htmlLabels: false,
  flowchart: { htmlLabels: false },
});

let seq = 0;

export async function mermaidToPng(code, scale = 2) {
  const { svg } = await mermaid.render(`mermaid-export-${seq++}`, code);

  // restore the real size from the viewBox (width="100%" leaves it undefined)
  const doc = new DOMParser().parseFromString(svg, "image/svg+xml");
  const root = doc.documentElement;
  const { width, height } = root.viewBox.baseVal;
  root.setAttribute("width", String(width));
  root.setAttribute("height", String(height));
  root.removeAttribute("style");
  const source = new XMLSerializer().serializeToString(root);

  const img = new Image();
  img.src = "data:image/svg+xml;charset=utf-8," + encodeURIComponent(source);
  await img.decode();

  const canvas = document.createElement("canvas");
  canvas.width = Math.ceil(width * scale);
  canvas.height = Math.ceil(height * scale);
  const ctx = canvas.getContext("2d");
  ctx.fillStyle = "#ffffff"; // a transparent PNG makes black text unreadable on dark backgrounds
  ctx.fillRect(0, 0, canvas.width, canvas.height);
  ctx.drawImage(img, 0, 0, canvas.width, canvas.height);

  return new Promise((resolve, reject) => {
    try {
      canvas.toBlob((blob) => (blob ? resolve(blob) : reject(new Error("PNG encode failed"))), "image/png");
    } catch (error) {
      reject(error); // some environments may refuse the export for security reasons
    }
  });
}

If you hand off the SVG itself, render it the same way with htmlLabels: false and reset width and height, and it will hold up better in other apps. When placing SVGs in Word or PowerPoint, also provide a PNG for environments without SVG support.

In Chrome, drawing an SVG containing foreignObject onto a canvas still allowed PNG export. Other browsers may render SVG or restrict canvases differently, so handle toBlob failures and test in the browsers your users have.

Pre-export checklist


To put Mermaid diagrams into a Word document, you can use the MD to DOCX converter in Markdown Document Converter. It applies the fixes from this article and embeds Mermaid diagrams in the DOCX as SVG with <text> labels, plus a PNG fallback. Conversion runs in your browser.

Convert Markdown to Word (DOCX)

Related articles: How to Export Mermaid Diagrams from Markdown to PDF / Why Markdown-to-Word (DOCX) Output Breaks in JavaScript / Markdown + Mermaid Docs: Git Diffs and Team Workflows