mermaid-js/mermaid · error · Error
svg element not in render tree
Error message
svg element not in render tree
What it means
Thrown inside calculateTextDimensions when getBBox() on a freshly appended SVG text element returns width 0 AND height 0 — the SVG element is not in the render tree, so the browser cannot measure it. calculateTextDimensions appends an <svg> to document.body, draws text, measures, then removes it; if the document isn't a live rendering DOM (or the SVG never attaches), getBBox yields zeros.
Source
Thrown at packages/mermaid/src/utils.ts:728
const g = body.append('svg');
for (const fontFamily of fontFamilies) {
let cHeight = 0;
const dim = { width: 0, height: 0, lineHeight: 0 };
for (const line of lines) {
const textObj = getTextObj();
textObj.text = line || ZERO_WIDTH_SPACE;
// @ts-ignore TODO: Fix D3 types
const textElem = drawSimpleText(g, textObj)
// @ts-ignore TODO: Fix D3 types
.style('font-size', _fontSizePx)
.style('font-weight', fontWeight)
.style('font-family', fontFamily);
const bBox = (textElem._groups || textElem)[0][0].getBBox();
if (bBox.width === 0 && bBox.height === 0) {
throw new Error('svg element not in render tree');
}
dim.width = Math.round(Math.max(dim.width, bBox.width));
cHeight = Math.round(bBox.height);
dim.height += cHeight;
dim.lineHeight = Math.round(Math.max(dim.lineHeight, cHeight));
}
dims.push(dim);
}
g.remove();
const index =
isNaN(dims[1].height) ||
isNaN(dims[1].width) ||
isNaN(dims[1].lineHeight) ||
(dims[0].height > dims[1].height &&
dims[0].width > dims[1].width &&
dims[0].lineHeight > dims[1].lineHeight)View on GitHub (pinned to d93e9c88c0)
Solutions
- Run rendering/measurement in a real browser or a DOM with layout (e.g. Playwright/Puppeteer with full Chromium), not plain jsdom.
- For SSR, precompute text dimensions client-side or use a browser-based render endpoint and ship the resulting SVG.
- If you must use jsdom, polyfill getBBox (e.g. via canvas measureText) or skip text-dimension caching.
- Ensure calculateTextDimensions is only called after document.body is available and the document is visible.
Example fix
// before — SSR with jsdom: getBBox returns 0x0
const dims = calculateTextDimensions(label, cfg); // throws
// after — render in a real browser (Playwright)
const browser = await chromium.launch();
const page = await browser.newPage();
await page.setContent('<div id=d></div>');
const dims = await page.evaluate((l) => mermaid.calculateTextDimensions?.(l), label); Defensive patterns
Strategy: try-catch
Validate before calling
function canMeasureText(): boolean {
if (typeof document === 'undefined' || !document.body || !document.body.append) return false;
const svg = document.createElementNS('http://www.w3.org/2000/svg', 'svg');
document.body.appendChild(svg);
const t = document.createElementNS('http://www.w3.org/2000/svg', 'text');
t.textContent = 'x'; svg.appendChild(t);
const ok = t.getBBox().width > 0;
document.body.removeChild(svg);
return ok;
}
if (!canMeasureText()) throw new Error('current DOM cannot measure SVG text (use a real browser)'); Type guard
function isRenderableDom(d: Document): boolean { return typeof d.body?.append === 'function' && typeof (d.createElementNS('http://www.w3.org/2000/svg','svg') as any).getBBox === 'function'; } Try / catch
try { return calculateTextDimensions(text, cfg); } catch (e) { if (/not in render tree/.test(String(e))) { return { width: text.length * 7, height: 14, lineHeight: 14 }; } throw e; } Prevention
- Render in a real browser engine (Chromium via Playwright/Puppeteer), not jsdom, when text dims matter.
- For SSR, render client-side or precompute dimensions.
- Polyfill getBBox in jsdom if you must run there.
When it happens
Trigger: Running mermaid in jsdom/Node SSR where getBBox is unimplemented or returns zeros, in a detached document fragment, in a test environment without layout, or before the SVG is actually attached to document.body. The guard `if (!body.remove) return {0,0,0}` only catches missing remove(); it doesn't catch a present-but-not-laid-out DOM.
Common situations: Server-side rendering of mermaid diagrams (text dims need a real browser layout engine), jsdom-based unit tests, headless environments without a rendering pipeline, or calling text-measuring APIs before document.body exists. Real browsers don't hit this; non-rendering DOM hosts do.
Related errors
- Nodes and querySelector are both undefined
- Could not find a suitable point for the given distance
- Sandbox iframe #i${id} is missing its content document
AI-assisted analysis of mermaid-js/mermaid@d93e9c88c0 (2026-08-12).
Data as JSON: /api/errors/eb04e486f089a521.
Report an issue: GitHub.