How does Mermaid architecture-beta syntax work? A complete reference
A complete reference for Mermaid's architecture-beta syntax: the four building blocks (groups, services, edges, junctions), port-based edges, the L/R/T/B directions, the {group} edge modifier, junctions, deterministic layout, and how AWS icon slugs plug in.
Mermaid's architecture-beta diagram is built from four block types: groups, services, edges, and junctions. Declare a node as service id(icon)[Label] and nest it with "in parent". Connect nodes with port-based edges like db:R -- L:server, where ports are L, R, T, or B and arrowheads are optional (--, -->, <-->). AWS icons use aws: slugs.
Most diagram-as-code formats were built for flowcharts and sequence diagrams. Mermaid's architecture-beta type is different: it was added specifically to draw cloud and CI/CD architectures, where the picture is made of services that live inside groups — a database inside a VPC, an EC2 instance inside a subnet — connected by edges.
That makes the syntax small but unusual. It has only four building blocks, and the edge grammar in particular looks nothing like a flowchart arrow. This is exactly the part that LLMs get wrong and that newcomers misremember, so this post is a complete, accurate reference for every piece of architecture-beta syntax, with AWS examples throughout.
If you want a guided walkthrough rather than a reference, start with how to draw AWS architecture diagrams in Mermaid. If you already have the syntax down and want to publish, jump to rendering and embedding Mermaid AWS diagrams.
What are the building blocks of an architecture-beta diagram?
Every architecture-beta diagram is assembled from exactly four kinds of declaration:
| Block | What it represents | Example |
|---|---|---|
| group | A container — a region, VPC, subnet, or account | group vpc(aws:virtual-private-cloud-vpc)[VPC] |
| service | A node — a single resource or instance | service db(aws:arch-amazon-rds)[Database] |
| edge | A connection between two services | db:R -- L:server |
| junction | A four-way split where several edges meet | junction split1 |
The diagram begins with the keyword architecture-beta on its own line. After that, you declare groups, services, edges, and junctions in any order — with one rule: an id must be declared before another line references it. In practice that means declare your groups first, then the services inside them, then the edges between those services.
Icons go inside parentheses () and labels go inside square brackets []. Both are optional in raw Mermaid, but for a readable AWS diagram you will almost always supply both.
How do you declare a group?
A group is a container. The syntax is:
group {id}({icon})[{label}] (in {parent})?So a VPC declared at the top level looks like this:
architecture-beta
group vpc(aws:virtual-private-cloud-vpc)[VPC]The id (vpc) is how you reference the group elsewhere. The icon slug sits in parentheses, the human-readable label in brackets. The trailing in {parent} clause is optional and is what lets you nest one group inside another.
How do you nest groups and services?
Nesting is done with the in keyword, and it is the same for both groups and services. A subnet inside a VPC, with an EC2 instance inside that subnet:
architecture-beta
group vpc(aws:virtual-private-cloud-vpc)[VPC]
group public_subnet(aws:public-subnet)[Public Subnet] in vpc
service web(aws:arch-amazon-ec2)[Web Server] in public_subnetThe containment is purely textual — in vpc places public_subnet inside vpc, and in public_subnet places web inside the subnet. There is no limit that bites in practice: region inside account, VPC inside region, subnet inside VPC, service inside subnet is a perfectly normal four-level nesting.
This containment is also what a validator reads to catch mistakes — a managed service like S3 drawn inside a subnet is a modeling error, because S3 lives outside the VPC boundary. See common AWS architecture mistakes for the full list of placement rules.
How do you connect services with edges?
This is the part that trips everyone up, because it does not look like a flowchart arrow. The full grammar is:
{serviceId}{group}?:{T|B|L|R} {<}?--{>}? {T|B|L|R}:{serviceId}{group}?Read left to right, an edge is: a source service, the side it leaves from, the connector, the side it arrives at, and the target service. The simplest form connects the right side of one service to the left side of another:
db:R -- L:serverTwo things are doing work here. The :R and :L are ports — they say which side of each node the line attaches to. The -- in the middle is the connector, and it controls the arrowheads.
Arrowheads are optional. You add < before the left port and/or > after the right port:
| Connector | Meaning | Example |
|---|---|---|
| -- | Plain line, no arrowhead | igw:L -- R:nat |
| --> | Arrow into the target | subnet:R --> L:gateway |
| <-- | Arrow into the source | gateway:R <-- L:subnet |
| <--> | Bidirectional | internet:L <--> R:igw |
All four forms describe the same connection between the same two nodes — the arrowheads are presentational. A validator that reasons about your architecture treats the graph as undirected, so igw:L -- R:internet and igw:L <--> R:internet are equivalent for the purpose of "is the gateway connected to the internet."
A plain -- edge is just as valid as -->. If you draw a connection without an arrowhead and a tool silently ignores it, that is a parser bug, not your mistake — the line is a real edge and should be read as one.
What do the L, R, T, and B ports mean?
The port after each colon is one of four values — Left, Right, Top, Bottom — and it chooses which side of the node the edge attaches to:
| Ports | Resulting line |
|---|---|
| R -- L | Straight horizontal line (source right → target left) |
| B -- T | Straight vertical line (source bottom → target top) |
| T -- L | 90-degree elbow (out the top, into the left) |
Matching opposite ports on the same axis gives a straight line; mixing axes gives a right-angle elbow. Ports are a layout hint — they are how you nudge architecture-beta's automatic layout into the shape you want without a manual coordinate system.
How do you draw an edge between two groups?
By default an edge connects two services. To route a connection out of a group boundary instead — group-to-group, or service-in-one-group to service-in-another — add the {group} modifier after the service id:
service server(aws:arch-amazon-ec2)[Server] in group_one
service subnet(aws:arch-amazon-ec2)[Subnet] in group_two
server{group}:B --> T:subnet{group}This makes the edge leave group_one (next to server) and enter group_two (next to subnet). Two rules matter here:
- You cannot use a group's own id as an edge endpoint. Edges connect services and junctions, never groups directly.
- The {group} modifier only works on a service that actually sits inside a group.
What is a junction, and when do you need one?
A junction is a special node that acts as a four-way split point. It has no icon and no label — it exists so several edges can meet at a single clean intersection instead of crossing awkwardly.
architecture-beta
service left_disk(disk)[Disk]
service top_disk(disk)[Disk]
service bottom_disk(disk)[Disk]
junction center
left_disk:R -- L:center
top_disk:B -- T:center
bottom_disk:T -- B:centerDeclare it with junction {id} (in {parent})? — like services, junctions can be placed inside a group with in. Reach for one when three or more services share a common connection point, such as several private-subnet resources routing through a single NAT path.
How do you use AWS icons instead of the five built-ins?
Out of the box, architecture-beta ships only five generic icons: cloud, database, disk, internet, and server. They take no prefix:
service db(database)[Database]Real AWS icons come from an icon pack referenced by a provider:slug name. AWS slugs use the aws: prefix:
| Service | Correct slug |
|---|---|
| EC2 | aws:arch-amazon-ec2 |
| Lambda | aws:arch-aws-lambda |
| S3 | aws:arch-amazon-simple-storage-service |
| RDS | aws:arch-amazon-rds |
| Internet Gateway | aws:res-amazon-vpc-internet-gateway |
| NAT Gateway | aws:res-amazon-vpc-nat-gateway |
| Application Load Balancer | aws:res-elastic-load-balancing-application-load-balancer |
Note the pattern: top-level products use an arch- prefix and sub-components (gateways, endpoints) use a res- prefix. The intuitive guesses — aws:s3, aws:lambda, aws:ec2 — do not exist. This is precisely why LLMs hallucinate slugs: the short form a developer expects is not the real name. The fix is to ground the model in the real catalog, which we cover in getting LLMs to generate valid Mermaid AWS diagrams.
Stock Mermaid will not render any aws: slug unless an AWS icon pack is registered in that environment. LatixEngine registers all 856 AWS icons (plus 626 Azure and 216 GCP) into architecture-beta automatically, and publishes the full slug list at latixengine.com/icons.json so you can look up or paste the real names.
How do you keep the layout deterministic?
By default, architecture-beta produces a deterministic layout — the same source always renders with the same node positions, because the randomize option defaults to false. That is what you want for documentation that lives in version control: the diagram does not reshuffle every time you re-render it.
If you would rather let the layout engine try varied, potentially better-spaced arrangements, enable randomization via frontmatter:
%%{init: {"architecture": {"randomize": true}}}%%
architecture-beta
group api(cloud)[API]
service db(aws:arch-amazon-rds)[Database] in api
service server(aws:arch-amazon-ec2)[Server] in api
db:R --> L:serverWhat does a complete AWS diagram look like?
Putting every block together — account, region, VPC, public and private subnets, an Internet Gateway, a NAT, and EC2 instances connected with port-based edges:
architecture-beta
service internet(aws:res-internet-alt1-light)[Internet]
group account(aws:aws-account)[AWS Account]
group region(aws:region)[Region] in account
group vpc(aws:virtual-private-cloud-vpc)[VPC] in region
group public_subnet(aws:public-subnet)[Public Subnet] in vpc
service igw(aws:res-amazon-vpc-internet-gateway)[Internet Gateway] in vpc
service nat(aws:res-amazon-vpc-nat-gateway)[NAT Gateway] in public_subnet
service web(aws:arch-amazon-ec2)[Web Server] in public_subnet
group private_subnet(aws:private-subnet)[Private Subnet] in vpc
service app(aws:arch-amazon-ec2)[App Server] in private_subnet
internet:L <--> R:igw
igw:B <--> T:web
igw:L <--> R:nat
nat:B <--> T:appNotice the internet service is declared at the top level — it sits outside the account, because the public internet is not part of your AWS account. The Internet Gateway lives at the VPC level, and the NAT sits in the public subnet. Those placements are not cosmetic; they are the difference between a diagram that models a correct architecture and one that quietly encodes a mistake.
Where does architecture-beta still trip people up?
Three recurring issues are worth keeping in mind:
- The -beta suffix is real. The syntax can change between Mermaid releases. Pin your version, and re-check diagrams after upgrading. For anything published, export to SVG so the image is frozen.
- Icon packs are environment-specific. A diagram that renders perfectly in one tool shows broken placeholders in another that has no AWS pack registered. This is the single most common reason "my diagram works locally but not in GitHub" — see rendering and embedding.
- Syntactically valid is not architecturally valid. The parser will happily accept a database in a public subnet or a NAT with no route to the internet. Catching those needs rules, not grammar — paste the diagram into the editor at latixengine.com/editor to validate both the syntax and the architecture in one pass.
architecture-beta is a compact language: four blocks, four ports, four connector forms. Once the edge grammar clicks, the rest is just nesting services into the right groups — and that structure is exactly what makes these diagrams checkable, not just drawable.
Frequently asked questions
What are the building blocks of a Mermaid architecture-beta diagram?
There are four: groups (containers like a VPC or subnet), services (nodes like an EC2 instance or database), edges (the connections between services), and junctions (a four-way split point that lets several edges meet cleanly). Every diagram starts with the architecture-beta keyword on its own line, followed by these declarations in any order, as long as each id is declared before it is referenced.
How do you connect two services with an edge in architecture-beta?
Use the form sourceId:PORT -- PORT:targetId, for example db:R -- L:server. The port after each colon is one of L, R, T, or B and sets which side of the node the line leaves from. Arrowheads are optional: -- is a plain line, --> points at the target, <-- points at the source, and <--> is bidirectional. Direction does not change which nodes are connected, only how the line is drawn.
What do L, R, T, and B mean in a Mermaid edge?
They are the four ports of a node — Left, Right, Top, and Bottom — and they pick the side the edge attaches to. Matching opposite ports (R to L, or B to T) draws a straight line; mixing axes (for example T to L) draws a 90-degree elbow. Ports are a layout hint that lets you control how the diagram is arranged.
How do you draw an edge between two different groups?
Add the {group} modifier after the service id on whichever side leaves its group, for example server{group}:B --> T:subnet{group}. This routes the edge out of the group boundary rather than from the service directly. You cannot use a group's own id as an edge endpoint — the {group} modifier only works on a service that sits inside a group.
Why don't my AWS icons show up in an architecture-beta diagram?
Stock Mermaid ships only five icons — cloud, database, disk, internet, and server — so any aws: slug renders as a broken placeholder unless an AWS icon pack is registered in that environment. LatixEngine registers 856 AWS icons (plus Azure and GCP) into architecture-beta automatically, so slugs like aws:arch-aws-lambda render without any setup. Paste a diagram at latixengine.com/editor to see them.
Is architecture-beta stable enough to use in production diagrams?
The -beta suffix means the syntax may still change between Mermaid releases, so pin your Mermaid version and re-check diagrams after upgrades. The core grammar — groups, services, port-based edges, and junctions — has been stable since v11.1.0. For published documentation, export the rendered diagram to SVG so the image is not affected by future syntax changes; see our guide on rendering and embedding.
Paste any example in this post into the LatixEngine editor to render it with native cloud icons and validate it against AWS, Azure, and GCP best practices. No login, no install.
Open the editor →