Readable grammars at any width

Railroad diagrams that know how to wrap.

A TypeScript implementation of the align → wrap → justify algorithm from Automatic layout of railroad diagrams, based on its Scala reference implementation. Build diagrams directly or turn an Ohm grammar into standalone, themeable SVG.

Try the playground Explore examples Read the documentation

Turn this

Describe the syntax your way.

Build a model with the JavaScript DSL or convert an Ohm grammar.

JavaScript DSL

const value = alternatives(
  terminal("null"), terminal("true"),
  terminal("false"),
  nonterminal("number"), nonterminal("string"),
);
const member = sequence(
  nonterminal("string"), terminal(":"), value,
);

return sequence(
  terminal("{"),
  optional(zeroOrMore(member, terminal(","))),
  terminal("}"),
);

Ohm grammar

JSON {
  object = "{" (member ("," member)*)? "}"
  member = string ":" value
  value  = "null" | "true" | "false"
         | number | string
}

Into this standalone, responsive SVG

Railroad diagram showing the syntax of a JSON object
Generated output A wide JSON object diagram rendered without wrapping.

Width-aware

Sequences wrap into readable rows while branches, loops, alignment, and direction remain intact.

Small model

Compose terminals, nonterminals, sequences, choices, and repetitions with a focused TypeScript API.

Ohm and CLI

Convert Ohm grammar files to SVG or JSON, or generate a complete browsable grammar atlas.

Get started

Install it, or import it directly.

npm

Install the ESM-only package in a JavaScript or TypeScript project:

npm install @marianoguerra/railroad-diagrams
import {
  sequence, terminal, nonterminal,
  zeroOrMore, renderSvg,
} from "@marianoguerra/railroad-diagrams";

const diagram = sequence(
  terminal("["),
  zeroOrMore(nonterminal("value"), terminal(",")),
  terminal("]"),
);

document.querySelector("#diagram").innerHTML =
  renderSvg(diagram, {width: 480});

Browser ESM

No build step is required. Import the package from an ESM CDN in a module script:

<div id="diagram"></div>
<script type="module">
  import {
    sequence, terminal, nonterminal, renderSvg,
  } from "https://cdn.jsdelivr.net/npm/@marianoguerra/railroad-diagrams@0.1.3/+esm";

  const diagram = sequence(
    terminal("if"),
    nonterminal("condition"),
    terminal("then"),
    nonterminal("result"),
  );

  document.querySelector("#diagram").innerHTML =
    renderSvg(diagram, {width: 640});
</script>

The version is pinned so the example stays reproducible. Change 0.1.3 when upgrading.

Command line

railroad-diagrams svg grammar.ohm --rule Main --full -o Main.svg
railroad-diagrams json grammar.ohm -o grammar.json
railroad-diagrams stages 01-basic.ohm 02-expressions.ohm -o stages