MintPDF HTML & Markdown → PDF API · MCP native API Free converter Guides Free key
GUIDE

Mermaid diagrams and math in a Markdown-to-PDF API

2026-09-22

Most Markdown renderers treat a document as text, a heading, a list, a table. That covers most documents. It does not cover a system architecture in a design doc, a decision tree in a runbook, or an equation in a report. Those get pasted in as a screenshot, or left out.

Two libraries fix this properly: Mermaid for diagrams, KaTeX for math. The interesting part is not that you can wire them into a renderer. It is what happens if you wire them in carelessly.

The naive version taxes every request

KaTeX's fonts are around 300KB. Mermaid's renderer is closer to 3.5MB. Load both unconditionally and every single PDF, including a one-line invoice with neither diagrams nor equations, pays that cost. At any real volume that is not a rounding error, it is the majority of your render time and your egress bill, spent on documents that never asked for it.

The fix is a cheap check before the expensive one:

const hasMath = body.includes("katex");
const hasDiagram = body.includes('class="mermaid"');

Parse the Markdown first. Only attach the KaTeX stylesheet if the parsed output actually contains a KaTeX node. Only load Mermaid's script and run it in the page if a code fence was tagged mermaid. An invoice never touches either path. A document with one diagram and no math loads only the 3.5MB, not both.

Math and diagrams are solved differently, for a reason

Diagrams have to run in the browser. Mermaid takes a text description and lays out a graph, that layout logic has to execute somewhere, and headless Chromium is already open for the print step, so it happens there: the script is injected with page.addScriptTag, initialized, and run against every <pre class="mermaid"> block before the page is printed.

Math does not need a browser at all. KaTeX can render straight to HTML server-side, before Chromium ever sees the page:

katex.renderToString(token.text, { displayMode: true, throwOnError: false, output: "html" })

That runs once, in Node, during Markdown parsing. It is faster, and it means a document with only equations and no diagrams never launches the Mermaid path at all.

The syntax choice that protects invoices

The obvious choice for inline math is a single $, the way most editors and note apps do it. This renderer deliberately does not support that.

The reason is what these documents actually are. The most common input isn't a paper, it's an invoice or a report, and | Widget | $9.00 | is a completely ordinary row in a completely ordinary table. A single-dollar math tokenizer would read $9.00 | and try to typeset it as an equation, silently. Not an error. A wrong-looking price column, discovered by a customer.

So the syntax here is unambiguous instead of familiar: display math is $$... $$, inline math is \(...\). Neither collides with a currency symbol, a table cell, or code containing a literal dollar sign. The tradeoff is that anyone copying LaTeX from somewhere using single-dollar inline math has to convert it. That is a one-line find-and-replace. A silently mangled price table is not.

Seeing it work

Math and an untouched price row, in one document:

curl -X POST https://mintpdf.dev/v1/pdf \
  -H "Content-Type: application/json" \
  -d '{"markdown":"Invoice-safe: \\(E = mc^2\\) stays inline.\n\nDisplay block:\n\n$$ \\int_0^\\infty e^{-x^2}\\,dx = \\frac{\\sqrt{\\pi}}{2} $$\n\n| Item | Price |\n|------|------:|\n| Widget | $9.00 |\n"}' \
  --output math.pdf

A diagram, on its own:

curl -X POST https://mintpdf.dev/v1/pdf \
  -H "Content-Type: application/json" \
  -d '{"markdown":"```mermaid\ngraph TD\nA[Markdown in] --> B{Has a diagram?}\nB -->|No| C[Render immediately]\nB -->|Yes| D[Load Mermaid, render SVG]\n```\n"}' \
  --output diagram.pdf

Both equations render as real typeset math, the diagram renders as an SVG, and the price row stays a price row. No signup for the first few renders a day. The renderer is open source if you want to see the tokenizer rules in context, and the same document works through an MCP tool call if your agent is the one writing the Markdown.

Try it without signing up: curl -X POST https://mintpdf.dev/v1/pdf -d '{"markdown":"# Hello"}'
or add it to your MCP client with npx -y mintpdf-mcp. Full API reference →