Skip to content
MermaidViewer

Tools

Dot language vs mermaid, when the layout is the point

Dot language vs mermaid is a syntax argument I actually have at work. DOT is what Graphviz reads. Mermaid is the documentation language that also draws sequence diagrams and charts.

By MermaidViewer editorsUpdated 11 min read

dot language vs mermaid is a syntax argument I actually have at work. DOT is the language Graphviz reads. You describe nodes and edges, and you ask a layout engine to place them. Mermaid is a documentation language that includes flowcharts plus a lot of other pictures, and its flowchart dialect is the part that overlaps with DOT. I use Mermaid when the file lives in a README. I use DOT when a program is emitting a large graph and I want Graphviz's layout, not a hand-written story. This post is about those two languages. The product-shaped comparison lives on the Mermaid versus Graphviz page, and I am not going to rerun a pricing fight here.

Two ways to say "an edge"

A DOT graph is a list of statements. digraph means the edges have direction. -> is an edge. rankdir=LR asks for left to right. Node names are bare words unless you need quotes. That is the whole language for a small dependency sketch, and it has been stable for a long time. I am not going to attach a Graphviz version number to that sentence. If a flag you need is missing, check the version you have installed rather than trusting a blog to know your package manager.

Mermaid's flowchart dialect uses --> and a keyword for direction, flowchart LR or flowchart TD. Node labels can differ from ids. api[API] is an id of api and a label of API. DOT can do labels too, with label attributes, and it can do a great deal more than I use. The difference a README author feels is smaller than the difference a tooling author feels. For ten nodes, both files are short. For a thousand nodes emitted by a script, I want DOT. For a decision a teammate will edit by hand in Markdown, I want Mermaid.

Here is the dependency sketch in DOT. This fence is text, not a Mermaid diagram. Don't paste it into a mermaid block and then wonder why the preview failed. The languages are not dialects of each other. A -> edge is not a courtesy the Mermaid parser owes you.

text
digraph deps {
  rankdir=LR;
  app -> api;
  app -> db;
  api -> db;
  worker -> db;
  worker -> queue;
  api -> queue;
}

rankdir is layout, not meaning. The edges would mean the same thing top to bottom. I set it because a dependency sketch reads like a pipeline, left to right, in the docs I write. Graphviz has other layout engines besides the usual hierarchical one. neato is the one I reach for when the graph is closer to an undirected mesh and the hierarchical ranks look forced. I am not listing every engine. The docs for your install will. The point is that DOT expects you to care about layout as a separate choice. Mermaid expects the flowchart direction and then takes the rest.

The same edges in Mermaid

This is the picture I commit when the graph is documentation. Same edges. Mermaid ids. Labels only where the word in the box shouldn't be the id. The database gets a cylinder because I want a glance to separate storage from processes. That shape is a hint, not a schema.

mermaid
flowchart LR
    app[App] --> api[API]
    app --> db[(DB)]
    api --> db
    worker[Worker] --> db
    worker --> queue[Queue]
    api --> queue
Open in the live editor

Nothing here is a decision. It is a graph, which is why DOT can say it just as well. I still use Mermaid for this size because the file is docs/deps.md and the preview is the pull request. GitHub and the other Markdown hosts I use will render a Mermaid fence. They will not render a DOT fence. If the audience is the README, the host support decides it. A sharper layout that nobody can see in the review is a private diagram. Private diagrams go stale.

The flowchart syntax page is the Mermaid side of this overlap: directions, shapes, subgraphs. The cheat sheet is the short list when you forget whether a cylinder is [()] or something you half-remembered from DOT's shape=cylinder. They are different spellings. I keep a note in the README the first time a teammate pastes DOT into a fence. One example of each, side by side, stops the mix-up. A lecture about graph theory will not.

What Mermaid is matters here because Mermaid is not "DOT for Markdown." The flowchart type is one keyword. Sequence diagrams, class diagrams, and ER diagrams have no DOT equivalent I care about, because DOT is a graph language and those pictures aren't just nodes and edges. If your document needs a sequence, DOT was never the candidate. If your document is only boxes and arrows, both languages are candidates, and the host and the size should pick.

A decision DOT can also say

DOT can express a branch. You make a node, you give it a label that ends in a question mark, and you label the edges. Graphviz will not automatically draw a diamond just because you asked a question. You set a shape if you want one. Mermaid's {question} is a diamond because flowcharts have a convention that decisions look like diamonds. That convention is the documentation culture Mermaid comes from. DOT comes from graph layout. The diamond is optional there, which surprises people who think the languages are skins.

mermaid
flowchart TD
    req[Request] --> cache{In the cache?}
    cache -->|Yes| hit[Return cached]
    cache -->|No| api[Call the API]
    api --> db[(DB)]
    api --> store[Store the response]
Open in the live editor

I would write the DOT for this with a node attribute for the diamond and edge labels for yes and no. I am not going to pretend the attribute list is shorter or more readable for a hand edit. It isn't, for me. The Mermaid form is the one I want a teammate to patch at 6 p.m. The DOT form is the one I want if this graph is one of hundreds a script emits, and the diamond is a shape I set in a node template.

Notice the Mermaid file still has no node named end. store is the last step. end is reserved as a subgraph closer. DOT will let you name a node end without that collision, because DOT's blocks close with braces. That is a real syntax difference, small and annoying. If you translate by hand, rename end to something that isn't a Mermaid keyword. I have broken a translated file exactly that way, and the error points at a line that looks innocent.

The schematic post is what I switch to when this flowchart's layout becomes the problem. A cache decision is a flow. A row of services is a schematic, and Mermaid's block diagrams hold rows still in a way neither a flowchart nor a default DOT rank will promise. DOT can lock ranks too, with subgraphs and rank constraints, and those constraints are more powerful and easier to get wrong. I use them when I am already in a DOT file for other reasons. I don't start a README there.

Layout control versus a host that renders

DOT's layout control is the reason it is still here. You can ask for ranks, you can pin a node, you can tell an edge to prefer a direction, you can run a different engine on the same file and get a different drawing of the same graph. When the picture is the output of a compiler pass or a package dependency explosion, that control is the product. I will tolerate the syntax. I will check the version's docs before I invent an attribute. Attributes have been added over many years, and a flag that works on my laptop may be missing on a minimal CI image. Try the render. Don't cite me as the man page.

Mermaid's layout control is the direction, subgraphs, and a handful of styles. You do not get a promise that adding a node keeps the other nodes where they were. I treat that as acceptable in docs and unacceptable on a poster. For the poster I export a picture and stop editing the layout, or I leave Mermaid entirely. I don't spend an afternoon fighting a flowchart engine to match a slide. That afternoon is a sign I wanted DOT's rank controls or a canvas, and I should switch instead of adding invisible nodes to shove things around. Invisible nodes are how both languages get cursed. I have written them. I have deleted them a month later after they confused someone.

Host support runs the other direction. A Mermaid fence in a README is a normal code block with a special tag. A DOT file needs Graphviz installed, or a service that installs it, or a committed SVG you regenerate. The render notes cover the Mermaid side of "it works on my machine and not in the pull request." DOT's version of that sentence is the CI image. Both are solvable. They are solved in different pipelines. If you don't have a pipeline and you do have a Markdown host, Mermaid is the smaller hammer. Use the smaller hammer until the graph gets big.

The syntax overview is the Mermaid spelling list when you are translating and you want to know what the target language can even say. Not every DOT attribute has a Mermaid twin. Line styles, ports on specific sides of a node, and precise rank constraints are the usual losses. If the diagram depended on a port, the Mermaid version is a different diagram. Say so in the commit. Don't call it a faithful conversion.

Large node sets

My cutoff is not a magic count. I don't have a benchmark I am willing to publish, and I don't trust round numbers I haven't measured on the graph in front of me. The qualitative cutoff is: did a person write these nodes, or did a program? A person writing more than a few dozen nodes by hand is probably documenting the wrong layer. They wanted a subgraph of the interesting part, and they drew the whole mesh because the tool allowed it. Switch to the interesting part. Mermaid will cope, and the reader will cope.

A program emitting hundreds or thousands of edges should not target a hand-maintained Mermaid file. Emit DOT. Let Graphviz lay it out. Commit the DOT if the generator's input isn't enough to reproduce it, or commit the generator and treat the DOT as a build artifact. Committing both a generator and a hand-edited DOT file is the dual-source bug. Pick one. I pick the generator when the graph is data, and I pick a small Mermaid summary when humans need a story about that data. The summary is allowed to omit nodes. A layout of every node is allowed to be ugly. They answer different questions.

Labels on a large DOT graph need a plan. Graphviz will happily draw a node whose label is a full file path, and the picture becomes a stripe of text. Truncate in the generator. Mermaid has the same disease if you paste those labels into a flowchart. The language isn't the fix. The label policy is. Short ids, a table under the figure for the long names, or the picture is a poster nobody reads.

Subgraphs in both languages are how you say "this cluster is a package." In DOT, a subgraph cluster_x is a real layout hint, and the cluster_ prefix is part of the convention Graphviz uses to draw a box around them. I mention that because people drop the prefix and then wonder why the box disappeared. Check your version's docs if the convention doesn't match what you see. In Mermaid, a subgraph is a visual group and a direction scope. Similar intent, different rules. Translate the intent, then fix the rules, instead of pasting cluster_ into a Mermaid file and hoping.

Where each file goes

README, design doc, pull request, anywhere a person edits by hand and a Markdown preview exists: Mermaid. I include the decision diagram and the small dependency sketch. I link the free diagram tools discussion if someone wants the wider menu of whiteboards and office canvases. Those aren't DOT either. They are a third place, for pictures that aren't source.

Build output, compiler dumps, dependency explosions, anything a script emits: DOT, rendered by Graphviz in the build, SVG committed or uploaded as an artifact. I don't paste that SVG's source back into Mermaid to "standardize." Standardization would destroy the layout and hide the generator. The standard that matters is knowing which file is generated.

A mixed repo is normal. docs/*.md holds Mermaid. graphs/*.dot holds DOT. A one-line note at the top of each DOT file says what emits it. A one-line note above a Mermaid fence says it is hand-written. Those notes prevent the helpful rewrite. I have been the helpful rewrite. I turned a generated graph into a flowchart, lost the ranks, and spent a review explaining why the picture moved. The edges were the same. The readers didn't care. They cared that the database had jumped.

If you are translating one small graph today, start from the Mermaid flowchart above and only write the DOT if you have a reason: a layout bug you cannot accept, a generator that already speaks DOT, or a host that will never render Mermaid. "DOT is more serious" is not a reason. Serious is the file that will still match the code in a month. For a hand-written README, that file is usually the fence.

Open the editor and paste the dependency flowchart. If you want the DOT side by side, keep it in a text file and render it with Graphviz locally. Don't drop the digraph block into the Mermaid preview. It will fail, and the failure doesn't mean the edges were wrong. It means the languages stayed different, which is what you want when each one has a job.

Frequently asked questions

Is DOT the same product as Graphviz?

DOT is the language. Graphviz is the tool that lays it out. People say the names interchangeably. The comparison page covers the products. This page covers the syntax choice.

Can Mermaid render a DOT file?

No. You rewrite a small graph by hand or keep the generator in DOT. Don't expect an editor to translate ten thousand edges.

Which one belongs in a README?

Mermaid, if the host renders it and a person maintains it. DOT, if a program writes the graph and CI publishes the SVG.