Quick start

Add the dependency, parse a diagram, render it. Three calls, no build step, no browser.

Install

// build.sbt — JVM
libraryDependencies += "rocks.earlyeffect" %% "mermoid" % "<version>"

// Scala.js (or a cross-built project)
libraryDependencies += "rocks.earlyeffect" %%% "mermoid" % "<version>"

// Optional: hybrid HTML + SVG for Specular / ascent apps
libraryDependencies += "rocks.earlyeffect" %% "mermoid-ascent" % "<version>"
libraryDependencies += "rocks.earlyeffect" %%% "mermoid-ascent" % "<version>"

Pre-1.0 on early-semver: pin the exact version and read the release notes before bumping the minor.

Parse and render

Mermaid("...") parses a literal while your code compiles: a broken diagram is a compile error at the literal, and the parsed Diagram is built into the call site. For text that arrives at runtime, Mermaid.from (or MermaidParser.parse) returns Either[ParseError, _]. Surface ParseError.message rather than swallowing the Left. SvgRenderer.render turns a Diagram into the SVG document. A literal, rendered:

MermoidAscent.svgDiagram(Mermaid("""flowchart LR
    A[Start] --> B{Ready?}
    B -->|yes| C([Ship it])
    B -->|no| A
""".stripMargin))
yesnoStartReady?Ship it

Write it to a file

On the JVM, the whole job is one Files.writeString:

import _root_.mermoid.*
import java.nio.file.{Files, Path}

def renderToFile(mmd: Path, svg: Path): Either[ParseError, Unit] =
  MermaidParser
    .parse(Files.readString(mmd))
    .map(d => Files.writeString(svg, SvgRenderer.render(d)))
    .map(_ => ())

Or skip the code and use the CLI.

Render in the browser

The same artifact cross-builds for Scala.js, so a Scala.js app can render a diagram client-side without pulling in mermaid.js:

import _root_.mermoid.*
import org.scalajs.dom

MermaidParser.parse(source).foreach { d =>
  dom.document.getElementById("chart").innerHTML = SvgRenderer.render(d)
}

If you build a virtual DOM rather than setting innerHTML, use SvgRenderer.renderTree and map the SvgNode tree to your framework's element type — no string round-trip.

SvgRenderer is deterministic: the same source and config produce the same SVG on the JVM and on Scala.js, which is what lets you render server-side and hydrate that markup without a mismatch.

MermoidAscent.diagramFitting is the browser layout. It paints, then follows the container with a resize observer and, when document exists, measures text from the canvas. MermaidBlocks.install(dom.document) replaces <pre class="mermaid"> and <div class="mermaid"> with that fitted diagram. The scope you install it in unmounts them.

Configure

RenderConfig is the one knob:

RenderConfig(
  layout            = LayoutConfig(),           // spacing, font sizes, shape geometry
  theme             = css.ThemeName.Default,    // Default | Dark | Forest | Neutral
  customStylesheet  = None,                     // merged over the theme
  resolveVariables  = true,                     // false keeps var(--mermoid-*) in the output
  responsive        = ResponsiveConfig(),       // keep authored direction; fit down to scale 0.5
)

Pass an optional Viewport(maxWidth) (and optionally maxHeight) to SvgRenderer.render / DiagramLayout.scene when you want spacing compressed toward a host width. That width does not change direction. Set flipDirectionBelow to opt into a flip. See Responsive layout for direction, spacing, and container fit.

See Theming for themes and Custom CSS for customStylesheet and resolveVariables.

Layout without painting

DiagramLayout.scene returns a Scene: Ranked for a flowchart or state diagram (geometry, edge routes, notes, click interactions) and Sequence for a sequence diagram (columns, lifelines, messages). Use it when you paint yourself, or when you only need metrics:

import _root_.mermoid.*

val scene: Either[ParseError, Scene] =
  MermaidParser.parse(source).map(d => DiagramLayout.scene(d, RenderConfig(), Some(Viewport(640))))

mermoid-ascent consumes the same scene for hybrid HTML + SVG; see Interactive.

Next steps