Why this deep dive exists
Everything so far has been about WHAT you draw. This deep dive is about the machine that draws it: how configuration flows from site defaults down to a single diagram, how to bring real icons and better layout engines into play, and where the security model draws its lines. Honest framing: two features here (ELK layout and KaTeX math) need extra setup beyond a plain CDN script tag - we show the source and say so, instead of pretending. Everything else on this page renders live.
Config layers: who wins 9 min
Mermaid builds each diagram's render config from three sources, applied in order: built-in defaults, then the site-wide mermaid.initialize() call, then per-diagram config from the author (frontmatter, or the older %%{init}%% directives). Later layers override earlier ones - except a short list of secure keys that only the site owner controls.
Self-studyFrontmatter: one diagram goes dark3 min▶
Frontmatter is a YAML block between --- fences at the very top of the diagram. Its config: key can override almost the entire configuration for that one diagram. Proof, live: this page's initialize() sets the pink theme, and the diagram below flips itself to the dark theme anyway - while every other diagram on the page stays pink.
---
config:
theme: dark
---
flowchart LR
ev["App events"] --> ing["Ingestion"]
ing --> dw[("Warehouse")]
dw --> bi["Dashboards"]
- The order matters: frontmatter must be the FIRST thing in the block - the
---fence before the diagram keyword. - title lives here too:
title: My diagramin frontmatter is the modern way to caption a diagram. - Scope is one diagram. Ten diagrams on a page, one frontmatter block each, zero interference.
Self-studyDirectives: the legacy dial (still everywhere)2 min▶
Before frontmatter existed, per-diagram config went in a %%{init: {...}}%% directive on the first line. Deprecated since v10.5, but still parsed - and you WILL meet it in older READMEs and Stack Overflow answers, so read it fluently:
%%{init: { "theme": "forest" } }%%
flowchart LR
a["Same trick"] --> b["older syntax"]
b --> c["still parses in v11"]
%%{init: { "theme": "forest" } }%% is exactly --- config: theme: forest --- in frontmatter. When you touch an old diagram, migrate the directive while you're there - same layer, same precedence, nicer diff.
Self-studyPer-diagram overrides you'll actually use2 min▶
Theme is the party trick; the workhorse overrides are diagram-type settings. Nesting under config: mirrors the config schema - here, flowchart edges become right-angled steps for that circuit-board look, on this one diagram only:
---
config:
flowchart:
curve: step
---
flowchart LR
raw["Raw zone"] --> stg["Staging"]
stg --> mart["Marts"]
stg --> ml["Feature store"]
Icons: logos in your boxes 7 min
Architecture diagrams ship with five built-in icons - cloud, database, disk, internet, server - and those render anywhere mermaid does. The upgrade is registering an iconify pack (200,000+ icons, including every vendor logo you deploy) so your warehouse box can wear the actual Snowflake or Redshift mark.
Self-studyThe built-ins, live: Nova as an architecture diagram3 min▶
Architecture diagrams place service nodes (each with an icon) inside group boxes, and wire them edge-to-edge with side anchors: L, R, T, B. The five default icons need no registration at all:
architecture-beta group nova(cloud)[Nova platform] service ing(server)[Ingestion] in nova service dw(database)[Warehouse] in nova service lake(disk)[Object storage] in nova service web(internet)[Dashboards] ing:R -- L:dw dw:B -- T:lake dw:R -- L:web
- Icon goes in parentheses, label in brackets:
service dw(database)[Warehouse]. - Edges anchor to sides:
ing:R -- L:dwmeans "ingestion's right side to warehouse's left side" - you're steering geometry, not just topology. - Groups nest services with
in group_id- the platform boundary draws itself.
Self-studyRegistering real icon packs (self-hosted power)3 min▶
On a page you control, register any iconify pack once and reference its icons as packname:icon-name in architecture diagrams (and flowchart icon shapes). The docs' own recipe, fetching the logos pack from a CDN:
Then in any architecture diagram on that page:
logos:airflow to a README and you'll get a placeholder, not the logo. Design READMEs around the five built-ins; save vendor logos for self-hosted pages.
A platform team's internal docs site registered the logos pack in its mermaid setup. Every architecture diagram suddenly showed the real Kafka, Snowflake and Airflow marks - and new joiners stopped asking "which box is the warehouse?" The same diagrams in GitHub fall back to plain boxes: readable there, gorgeous on the docs site, one source for both.
Layout engines: dagre vs ELK 7 min
Every flowchart so far was laid out by dagre, mermaid's default engine - fast, fine, occasionally spaghetti. ELK (Eclipse Layout Kernel) is the heavyweight alternative: better edge routing, fewer crossings, calmer results on big graphs. Switching is one line of frontmatter... where ELK is available.
Self-studyOne line to switch - and where it works3 min▶
The switch itself is beautifully boring:
@mermaid-js/layout-elk) that self-hosted pages must register alongside mermaid - which is why the block above is source, not live: this course's plain CDN setup doesn't include it. mermaid.live has ELK built in, so paste the block there and it just works. Other supported layouts in the docs: dagre (default), elk, tidy-tree, cose-bilkent.
Self-studyWhen ELK earns its keep2 min read▶
- Many edge crossings - the classic data lineage graph where every source feeds every mart. ELK's routing untangles what dagre leaves knotted.
- Long ranks - wide layers (20 tables in one tier) that dagre spreads into a football field. ELK packs them more sensibly.
- Dense subgraph nesting - multiple zones with cross-zone edges tend to route cleaner under ELK.
- Not worth it below ~15 nodes: for small diagrams the engines produce near-identical results, and dagre needs zero extra setup. Try dagre first, reach for ELK when the diagram visibly fights you.
A 40-table lineage diagram for a warehouse migration was unreadable under the default layout - edges everywhere. One frontmatter line switching to ELK on mermaid.live turned it into something the team could actually walk through in the review meeting. Same source, different brain doing the geometry.
Math in labels, and the security dial 8 min
Two under-the-hood topics that share a theme: what mermaid will and won't render for you. KaTeX puts real equations in flowchart and sequence labels; securityLevel decides how much your diagrams are allowed to do - including whether click links work at all.
Self-studyKaTeX: equations in labels3 min▶
Wrap an expression in $$...$$ inside a quoted label and mermaid hands it to the KaTeX typesetter (v10.9+). Supported in flowcharts and sequence diagrams - a lovely fit for ML pipeline docs where the transformation IS a formula:
forceLegacyMathML config plus KaTeX's stylesheet gives consistent output when that matters.
Self-studysecurityLevel: the dial behind everything3 min▶
Diagrams are user input, and mermaid treats them that way. The securityLevel setting - a SECURE key, so only initialize() can set it, never a pasted diagram - has four positions:
| Level | What it means | Use when |
|---|---|---|
strict | default - HTML in labels is encoded, click interactions disabled | rendering diagrams you didn't write (the safe default; this page uses it) |
loose | HTML allowed, click links and callbacks enabled | trusted content where you want clickable diagrams |
antiscript | HTML allowed but script tags stripped | a middle ground for mostly-trusted content |
sandbox | everything renders inside a locked-down iframe | maximum paranoia; some interactive features stop working |
The everyday symptom: click statements that silently do nothing. A flowchart like this needs the SITE to be at loose - no frontmatter can rescue it, by design:
Try it yourself - this week ◐ 35-45 min total
- Take any diagram from your repos and flip just IT to the dark theme with frontmatter. Then migrate one old %%{init}%% directive you find in the wild to frontmatter - same layer, cleaner syntax.
- Rebuild your platform as an architecture-beta diagram using only the five built-in icons. Then, on a local HTML page, register the logos pack and swap in the real vendor marks - compare which version each audience deserves.
- Find (or make) a flowchart with 20+ nodes and real edge crossings, paste it into mermaid.live, and add layout: elk. Screenshot before and after - that pair is your argument for setting up ELK on the docs site.
- On mermaid.live, put one honest formula from your work into a flowchart label with $$...$$ - a loss function, a score threshold, anything real.
- Check the mermaid.initialize() call in any docs site you own: what securityLevel is it running? If you've ever wondered why click links die there, you now know where to look.
Official docs covered
This deep dive works through these sections of the official docs at mermaid.js.org (mermaid 11.x): the configuration, directives, icons, layouts and math pages under Config, plus the securityLevel setting from the usage guide.
Three questions before you go 🎯 ◐ 90 seconds
1 · The site's initialize() sets the pink theme. One diagram carries frontmatter with config: theme: dark. What renders?
Defaults, then initialize(), then per-diagram frontmatter - the author gets the last word on everything except secure keys. You watched it happen in Part 1.
2 · Your 40-node lineage flowchart is spaghetti. How do you hand layout to ELK?
layout: elk in frontmatter config is the whole switch. It works out of the box on mermaid.live; self-hosted pages must register @mermaid-js/layout-elk first.
3 · You added click statements to a flowchart on the docs site and nothing happens. Most likely cause?
securityLevel is a secure, site-level key: strict disables interactions by design, and only the initialize() call can loosen it. That lock is what makes rendering untrusted diagrams safe.