Diagram type

Mermaid C4 Diagram Syntax & Examples

Reference for Mermaid C4 diagram syntax — C4Context / C4Container / C4Component, Person, System, Boundary, and Rel declarations. Copy-paste architecture examples.

Syntax reference, layout guidance, and ready-to-open examples for this diagram type.

At a glance

A syntax reference for Mermaid's C4 diagram grammar — read this when you need the exact way to declare a person, system, container, or relationship at the four C4 levels (context / container / component / code), then preview and beautify the result in the editor.

What is the C4 modelThe four C4 diagram keywordsElement declarations — Person, System, Container, ComponentRelationships — Rel and labelled connectionsBoundaries — group containers and componentsCommon patterns & gotchasLayout — statement order vs automatic
Rendered proof
C4 container diagram for a ride-hailing platform — nested boundary, data store, event queue, and external dependencies.
Theme · Atlas
Open this diagram in editor
Ride-hailing platform [SYSTEM] Container diagram — ride-hailing platform Books trips withCalls[HTTPS / JSON]Reports location via[gRPC]Routes requests to[HTTP]Requests driver match[gRPC]Publishes trip events[Kafka]Reads and writes[SQL]Charges fares via[HTTPS]Consumed byRoutes and ETAs[HTTPS] Rider Requests and pays for trips Driver Accepts and fulfils trip requests Rider App [React Native] Trip booking, live tracking,payments API Gateway [Kong] Routing, auth, rate limiting Trip Service [Go] Matching, pricing, trip lifecycle Dispatch Service [Elixir] Driver location ingest andassignment Trip Store [PostgreSQL] Trips, fares, receipts Trip Events [Kafka] Trip state changes fanned outto consumers Notification Worker [Node.js] Push and SMS fan-out Maps Provider Routing and ETAs Payment Processor Card charges and driver payouts
View Mermaid sourcePlain-text diagram syntax — copy or edit directly.
diagram.mmd
1C4Container
2 title Container diagramride-hailing platform
3 Person(rider, "Rider", "Requests and pays for trips")
4 Person(driver, "Driver", "Accepts and fulfils trip requests")
5 System_Boundary(platform, "Ride-hailing platform") {
6 Container(riderApp, "Rider App", "React Native", "Trip booking, live tracking, payments")
7 Container(gateway, "API Gateway", "Kong", "Routing, auth, rate limiting")
8 Container(trips, "Trip Service", "Go", "Matching, pricing, trip lifecycle")
9 Container(dispatch, "Dispatch Service", "Elixir", "Driver location ingest and assignment")
10 ContainerDb(tripsDb, "Trip Store", "PostgreSQL", "Trips, fares, receipts")
11 ContainerQueue(events, "Trip Events", "Kafka", "Trip state changes fanned out to consumers")
12 Container(notify, "Notification Worker", "Node.js", "Push and SMS fan-out")
13 }
14 System_Ext(maps, "Maps Provider", "Routing and ETAs")
15 System_Ext(payments, "Payment Processor", "Card charges and driver payouts")
16 Rel(rider, riderApp, "Books trips with")
17 Rel(driver, gateway, "Reports location via", "gRPC")
18 Rel(riderApp, gateway, "Calls", "HTTPS / JSON")
19 Rel(gateway, trips, "Routes requests to", "HTTP")
20 Rel(trips, dispatch, "Requests driver match", "gRPC")
21 BiRel(dispatch, maps, "Routes and ETAs", "HTTPS")
22 Rel(trips, tripsDb, "Reads and writes", "SQL")
23 Rel(trips, events, "Publishes trip events", "Kafka")
24 Rel(events, notify, "Consumed by")
25 Rel(trips, payments, "Charges fares via", "HTTPS")

What is the C4 model

C4 is an architecture documentation model developed by Simon Brown that describes a software system at four levels of zoom: Context (the system and its external users / systems), Container (the deployable / runnable pieces — services, databases, queues), Component (the major code modules inside a container), and Code (the class-level detail, rarely diagrammed). Mermaid supports the first three cleanly with the `C4Context`, `C4Container`, and `C4Component` diagram keywords. The fourth level (Code) is typically expressed with class diagrams instead. Each C4 level is its own diagram; mixing levels in one chart breaks the cognitive model.

The four C4 diagram keywords

Each diagram starts with one of the C4 type keywords. `C4Context` is the outermost zoom — the system shown as a single box with external users and external systems around it. `C4Container` zooms in: the system is decomposed into its containers (services, databases, single-page apps). `C4Component` zooms in further: a single container's internal components are shown. `C4Dynamic` is a Mermaid-specific variant for showing dynamic behaviour at any C4 level. Pick the level by audience: Context for non-technical readers, Container for engineering planning, Component for deep dives.

  • `C4Context` — system + external actors (for outside audiences)
  • `C4Container` — decompose the system into deployable pieces
  • `C4Component` — decompose a single container
  • `C4Dynamic` — show runtime behaviour at any level

Element declarations — Person, System, Container, Component

Each level uses different element declarations. `Person(id, "Label", "Description")` declares a human actor. `System(id, "Label", "Description")` declares an internal system (your system). `System_Ext(id, "Label", "Description")` declares an external system you depend on (Stripe, an email service, a third-party API). `Container(id, "Label", "Tech", "Description")` declares a deployable piece — note the extra `Tech` parameter (e.g. "Node.js", "PostgreSQL"). `Component(id, "Label", "Tech", "Description")` works the same way inside a container. Suffix `_Ext` variants exist for every type to mark external elements with a distinct visual style.

  • `Person(id, "Label", "Description")` — human / actor (Person_Ext for external)
  • `System(id, "Label", "Description")` — internal system (System_Ext for external)
  • `Container(id, "Label", "Tech", "Description")` — deployable piece (Container_Ext for external)
  • `Component(id, "Label", "Tech", "Description")` — module inside a container

Relationships — Rel and labelled connections

Relationships use `Rel(from_id, to_id, "Label")` for the default arrow. `Rel(from, to, "Label", "Tech")` adds a technology / protocol annotation on the connection (e.g. "HTTPS / API", "AMQP", "JDBC"). Directional variants control the visual direction: `Rel_U` (up), `Rel_D` (down), `Rel_L` (left), `Rel_R` (right) — useful when the auto-layout doesn't match the intended reading direction. `BiRel` declares a bidirectional relationship without two separate arrows. Labelling relationships well is the highest-leverage part of a C4 diagram — readers learn the integration model from the labels, not from the box layout.

Boundaries — group containers and components

Boundaries group related elements visually. `System_Boundary(id, "Label")` and `Container_Boundary(id, "Label")` create a labelled enclosure that other elements are declared inside. Use `Enterprise_Boundary(id, "Label")` to wrap multiple systems that belong to the same organisation. Boundaries are particularly useful at the Container level to show service boundaries: which containers are deployed together, which cross an organisational boundary, which belong to the same product area. Like with subgraphs in flowcharts, prefer one or two levels of boundary nesting — deeper nesting becomes hard to read.

Common patterns & gotchas

Two patterns dominate real C4 diagrams. Context diagrams almost always have the shape: one System box in the centre, two-to-four Person boxes around it, and a few System_Ext boxes for third-party dependencies. Container diagrams typically show 3-8 Container boxes inside the system, with external Persons and Systems still on the periphery. The most common gotcha: mixing levels in one chart — putting both System and Container declarations in the same diagram breaks C4's discipline and produces an unclear hybrid. Second gotcha: forgetting the `"Tech"` parameter on Container / Component (the third positional argument) — the diagram still renders but the technology annotations that make C4 diagrams useful are missing.

  • Keep one C4 level per diagram — don't mix System with Container in the same chart
  • Always supply the `Tech` parameter on Container / Component for the integration annotations
  • Use `_Ext` variants for external (third-party) elements — visual distinction matters

Layout — statement order vs automatic

Mermaid's native C4 renderer has no layout algorithm: elements are packed into rows in the order you declare them, and the only controls are `UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1")` (how many shapes / boundaries fit per row) and reordering your statements until the diagram reads correctly. That works for small context diagrams but becomes a fight on anything with boundaries or more than a handful of relationships. Beauty Diagram's editor lays C4 out automatically instead: relationship structure determines placement, boundaries size themselves around their contents (including nested boundaries), and elements declared without any `Rel` are grouped beside related elements of the same kind rather than scattered. The same source renders both ways, so you can keep `UpdateLayoutConfig` lines in the file for native renderers — they're ignored here without error. C4-PlantUML documents are accepted directly too, `@startuml` wrapper and `!include` lines included.

  • Native Mermaid: layout follows statement order; tune with `UpdateLayoutConfig` and reordering
  • Beauty Diagram editor: automatic layout from relationships and boundaries — no manual tuning
  • `UpdateElementStyle` / `UpdateRelStyle` lines are accepted and safely ignored — themes own the styling
FAQ

Mermaid C4 Diagram Syntax & Examples — frequently asked questions

What's the difference between C4Context, C4Container, and C4Component in Mermaid?

Each is a different zoom level on the same system. `C4Context` is the outermost view — your system shown as a single box with external users and external systems around it. `C4Container` zooms inside: the system is broken into its deployable pieces (services, databases, single-page apps). `C4Component` zooms further into one container, showing its internal modules. Pick the level by audience: Context for non-technical stakeholders, Container for engineering planning, Component for deep architectural reviews. Mermaid also supports `C4Dynamic` for showing runtime behaviour at any level.

When should I use System_Ext vs System?

Use `System(id, "Label", "Description")` for systems you own and operate — the systems that are inside your team's scope. Use `System_Ext(id, "Label", "Description")` for third-party systems you depend on but don't control: Stripe, Auth0, an email provider, an external API. The `_Ext` suffix exists for `Person_Ext` (external user / customer outside your organisation) and `Container_Ext` (external container) too. The visual distinction is important — it tells readers what is in-scope for changes vs what is a fixed external dependency.

How do I label the technology on a C4 container?

The Container and Component declarations take a third positional argument for technology: `Container(api, "API Service", "Node.js / Hono", "Public REST API")`. This appears as a small annotation under the main label in the rendered diagram. The same pattern applies on relationships: `Rel(api, db, "Stores users", "PostgreSQL / JDBC")` adds the protocol or technology annotation to the connection. These technology labels are what make C4 diagrams useful for engineering planning — without them the diagram is just boxes.

Can I group containers visually inside a C4 diagram?

Yes — use boundaries. `System_Boundary(id, "Label")` wraps containers that belong to the same system. `Container_Boundary(id, "Label")` is for components inside a container. `Enterprise_Boundary(id, "Label")` is the outermost — wraps multiple systems that belong to the same organisation. Boundaries are declared as parent elements that contain child declarations inside them, like a subgraph in a flowchart. Don't nest boundaries deeper than two levels — the visual hierarchy becomes hard to follow.

Should I mix multiple C4 levels in one diagram?

No. The whole point of C4 is that each level has a different audience and a different level of abstraction — mixing them in one diagram produces an unclear hybrid that doesn't serve either audience well. Make a separate diagram per level: the Context diagram for the executive summary, the Container diagram for the planning doc, the Component diagram for the deep-dive design review. Link the levels in the surrounding documentation rather than stacking them together visually.

How do I control the layout of a Mermaid C4 diagram?

In native Mermaid, layout follows your statement order: elements pack into rows as declared, and `UpdateLayoutConfig($c4ShapeInRow="3", $c4BoundaryInRow="1")` changes how many fit per row. There is no automatic placement, so polishing a native C4 diagram usually means reordering declarations until it reads correctly. Beauty Diagram's editor takes the opposite approach — placement is computed from the relationship structure and boundary nesting, so reordering statements isn't needed and `UpdateLayoutConfig` is safely ignored. If a relationship arrow reads in the wrong direction conceptually, fix the `Rel(from, to, …)` argument order rather than the layout.

Why do my unconnected C4 elements end up in odd positions?

Elements declared without any `Rel` give a layout engine nothing to anchor them to, so most renderers either pack them in declaration order (native Mermaid) or push them to the canvas edge. Beauty Diagram groups unconnected elements beside connected elements of the same kind inside the same boundary — unconnected Persons line up next to the connected Person, spare databases sit with the other data stores. If an element genuinely belongs somewhere specific, the most reliable fix in any renderer is to give it at least one labelled relationship; the connection also tells readers why the element is in the diagram at all.

Does this work with C4-PlantUML sources?

Yes. Paste a C4-PlantUML document as-is — the `@startuml` wrapper, the `!include <C4/C4_Context>` line (any include spelling: the stdlib form, a relative path, or the raw GitHub URL), and directives like `LAYOUT_LEFT_RIGHT()` or `SHOW_LEGEND()` are all understood. The diagram level is read from the include name, `LAYOUT_LEFT_RIGHT()` flips the reading direction, and the styling directives are safely ignored in favour of themes. The same elements, boundaries, and relationships render identically whether they arrive as Mermaid C4 or C4-PlantUML.