npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

mermaid-layout-circular

v0.9.0

Published

Circular layout engine for mermaid flowcharts — nodes on a circle, edges as arcs

Readme

mermaid-layout-circular

A circular layout engine for mermaid flowcharts. Write an ordinary flowchart, set layout: circular, and the nodes are placed evenly around a circle with the edges drawn as arcs of that same circle. The rest of the flowchart language keeps working: node shapes, edge labels, css classes, and look: handDrawn.

Mermaid's default engine, dagre, is built for hierarchies. Given a cycle, it breaks the loop, lays the nodes out in a line, and routes one long arrow back around the outside. The result never looks like a cycle. This package addresses the request in mermaid-js/mermaid#3228, open since 2022, that cycles be represented more naturally.

Here is the same five-node flowchart rendered both ways — the two columns differ only in the layout: line:

---
config:
  layout: circular
---
flowchart LR
  E[Evaporation] --> C[Condensation]
  C --> P[Precipitation]
  P --> R[Runoff]
  R --> O[Collection]
  O --> E

| layout: circular | layout: dagre (mermaid default) | | --- | --- | | The water cycle as a ring of five boxes connected by arcs of one circle | The same five nodes flattened into a horizontal ladder with a long return arrow |

Edge labels live in the gaps between boxes, and a chord between non-neighbors bows through the middle:

---
config:
  layout: circular
---
flowchart LR
  W[Wake] -->|coffee| Wk[Work]
  Wk -->|lunch| M[Meetings]
  M -->|escape| F[Focus]
  F -->|dusk| Hm[Home]
  Hm -->|sleep| W
  Wk -.->|skip the day| Hm

A five-node daily cycle with labels in the gaps and a dotted chord across the middle

Mermaid's hand-drawn look keeps working too:

---
config:
  layout: circular
  look: handDrawn
  theme: neutral
---
flowchart LR
  L[Listen] --> T[Think]
  T --> S[Speak]
  S --> H[Be heard]
  H --> L

A four-node cycle drawn in mermaid's sketchy hand-drawn style

A cycle with side branches keeps its ring: the cycle stays on the circle and everything else hangs off it radially, the way textbook figures would draw the Krebs cycle, for example:

---
config:
  layout: circular
---
flowchart LR
  A[Citrate] --> B[Isocitrate] --> C[Ketoglutarate] --> D[Succinyl-CoA]
  D --> E[Succinate] --> F[Fumarate] --> G[Malate] --> H[Oxaloacetate] --> A
  AcCoA[Acetyl-CoA] --> A
  C --> CO2a[CO2]
  D --> CO2b[CO2 again]
  G --> NADH

An eight-node Krebs cycle ring with Acetyl-CoA feeding in from above and CO2 and NADH branching outward

A hub earns the center. When one node is unmistakably the middle of the diagram — the center of a star, or the axle of a wheel whose ring survives without it — it moves to the origin and everything else rings around it, with the spokes drawn straight from border to border. Anything hanging off a spoke keeps hanging outward:

---
config:
  layout: circular
---
flowchart LR
  Hub[Registry] --> A[Alpha]
  Hub --> B[Bravo]
  Hub --> C[Charlie]
  Hub --> D[Delta]
  Hub --> E[Echo]
  B --> L[Bravo's log]

A registry hub centered among five ringed spokes, with a log node hanging outward off one spoke

A wheel keeps its ring:

---
config:
  layout: circular
---
flowchart LR
  A[Plan] --> B[Build] --> C[Test] --> D[Ship] --> E[Watch] --> F[Learn] --> A
  A --> Hub[Vision]
  B --> Hub
  C --> Hub
  D --> Hub
  E --> Hub
  F --> Hub

A six-node plan-build-test ring with a vision node at the axle, fed by six straight spokes

The detection is deliberately conservative — a path, a balanced tree, a plain ring, or a ring with a chord or two never elects a hub. But spokes have special status: a node adjacent to every other ring member holds the center even when the outer ring is missing arcs, and a wheel that meshes a gear off one of its rim members keeps its axle too — both at once, here:

---
config:
  layout: circular
---
flowchart TB
  P[Captain
  Planet!]
  Earth <--> P
  Fire <--> P
  Wind <--> P
  Water <--> P
  Heart <--> P
  Heart <--> Water
  Heart <--> Earth
  Earth <--> Fire
  Fire <--> Wind
  Water <--> Vapor
  Vapor <--> Ice
  Ice <--> Water

Captain Planet centered among five ringed elements, the Water-Wind arc absent, with a Vapor-Ice gear meshing off Water

The hub option overrides the detection either way: 'none' keeps every node on the ring, a node id names the hub explicitly.

Circles hang off circles as circles. A pendant cycle is not flattened onto the main ring: it becomes a satellite — its own smaller circle, parked outside the rim on its anchor's ray:

---
config:
  layout: circular
---
flowchart LR
  P[Plan] --> B[Build] --> S[Ship] --> L[Learn] --> P
  B --> D[Debug]
  D --> F[Fix] --> T[Test] --> D

A four-node product loop with a debug triangle drawn as its own smaller circle beyond Build

Two cycles sharing a node draw as a figure-eight, the circles genuinely tangent at the shared node — and the smaller circle flows against the main ring, the way meshing gears turn:

---
config:
  layout: circular
---
flowchart LR
  W[Wake] --> K[Work] --> N[Dine] --> S[Sleep] --> W
  S --> R[Dream] --> T[Toss] --> S

Two circles meeting at a shared Sleep node, the day loop large and the dream loop small

The largest cycle keeps the middle, and the nesting recurses — a circle hanging off a circle hanging off a circle walks outward, each ring a true circle of its own, chains of meshed loops alternating direction like a gear train:

---
config:
  layout: circular
---
flowchart LR
  A[Mine] --> B[Smelt] --> C[Cast] --> D[Sell] --> E[Invest] --> A
  B --> F[Scrap]
  F --> G[Sort] --> B
  F --> H[Shred]
  H --> I[Melt] --> F

A five-node ring meshed with a second loop, which is meshed with a third, each spinning against the last

Usage

Select the layout per diagram in frontmatter, the same way @mermaid-js/layout-elk works:

---
config:
  layout: circular
---
flowchart LR
  A --> B --> C --> D --> A

Register the layout once, before rendering:

import mermaid from 'mermaid';
import circularLayouts from 'mermaid-layout-circular';

mermaid.registerLayoutLoaders(circularLayouts);

Registration adds two layout names. layout: circular walks the ring clockwise from the top; layout: circular-ccw walks it counter-clockwise — the same layout seen in a mirror, so every guarantee about spacing, symmetry and arrows carries over unchanged:

---
config:
  layout: circular-ccw
---
flowchart LR
  E[Evaporation] --> C[Condensation]
  C --> P[Precipitation]
  P --> R[Runoff]
  R --> O[Collection]
  O --> E

The water cycle running counter-clockwise: Evaporation on top, Condensation to its left, the arrows flowing leftward around the ring

Setting direction: 'counterclockwise' in the options (below) does the same for diagrams that say layout: circular; the frontmatter name wins when both are given.

Subgraphs draw as boxes around their members. On the ring, a group's members are seated side by side so the box wraps one arc:

---
config:
  layout: circular
---
flowchart LR
  E[Evaporation] --> C[Condensation]
  subgraph Atmosphere
    C --> P[Precipitation]
  end
  P --> R[Runoff]
  subgraph Land
    R --> O[Collection]
  end
  O --> E

The water cycle with Atmosphere and Land boxes each wrapping two adjacent nodes of the ring

A subgraph around a pendant cycle wraps the whole satellite:

A product loop with the debug triangle drawn as a satellite circle inside its own titled box

An edge pointing at a subgraph itself (rather than a node inside it) is not routed yet; it is dropped with a warning. Diagram types other than flowcharts are out of scope, since each one owns its layout.

To use the layout in Obsidian, install obsidian-mermaid-circular, a small plugin that registers it with the mermaid instance Obsidian already bundles. Diagrams in notes then opt in with the same frontmatter.

Options

Mermaid's config schema has no slot for layout engine options, so the knobs are set in code, next to registration:

import { setCircularLayoutOptions } from 'mermaid-layout-circular';

setCircularLayoutOptions({ spacing: 60, bow: 0.4 });

The available options and their defaults live in CircularLayoutOptions and defaults in src/layout.ts. Every default was chosen by rendering the alternatives and looking at them. The screenshots that drove those choices are committed in trials/, and the verdicts are recorded in docs/DECISIONS.md. When no option overrides it, spacing is seeded from mermaid's own flowchart.nodeSpacing.

Demo

npm run dev serves the demo. The gallery at / renders ten cases, and /trials.html?suite=bow (also swerve, ordering, spacing, samples) renders the same diagrams under different option values for side-by-side comparison.

Development

Three gates, all green before committing changes to src/:

npm test        # vitest, the placement math
npm run lint    # eslint
npm run build   # vite library build plus type declarations

Caveats

The package renders through mermaid's InternalHelpers, which mermaid marks as deprecated for external use. The official layout engines (elk, tidy-tree) ship on the same seam, so the risk is shared, but a mermaid upgrade in a consuming project is the right moment to re-run the demo and look. The peer range is mermaid ^11.12.0, the earliest version verified to carry the internals this package relies on. Developed against 11.16.

One more limitation worth knowing: mermaid measures HTML edge labels with getBoundingClientRect, which reports screen pixels. If the rendering container is scaled by a CSS transform, label collision checks will be off by that scale factor.

License

MIT