By·

Mermaid ZenUML: Modern Sequence Diagrams in Plain Text

Learn Mermaid ZenUML — the cleaner, code-like alternative to classic Mermaid sequence diagrams. Covers participants, sync/async messages, loops, alt/opt/par fragments, and real-world API flow examples.

Rendered Mermaid ZenUML: Modern Sequence Diagrams in Plain Text
Rendered example. Copy the first code block below to edit it.

# Mermaid ZenUML: Modern Sequence Diagrams in Plain Text

ZenUML is Mermaid's newer, code-flavored answer to sequence diagrams. If you find the classic Mermaid sequence diagram syntax a bit verbose — all those arrows, activate/deactivate pairs, and explicit note placements — ZenUML strips it down to something that reads like pseudocode. Same diagrams, less ceremony.

Originally a standalone open-source library, ZenUML was merged into Mermaid.js in v10.4+. It uses a programming-language-inspired syntax where participants talk to each other through method calls, not arrow tokens.

Why ZenUML Over Classic Mermaid Sequence Diagrams?

Classic MermaidZenUML
`A->>B: doSomething()``A.doSomething()`
Manual activate/deactivateAutomatic lifelines
`loop ... end``while(condition) { }`
`alt ... else ... end``if(x) { } else { }`
Separate note syntaxInline `// comment`

If you already write code, ZenUML feels natural immediately. No arrow memorization, no activation management, no separate block terminators.

Getting Started: Your First ZenUML Diagram

The diagram type is zenuml. Participants appear automatically when first referenced:

zenuml
Client.OrderService.createOrder()
OrderService.PaymentGateway.charge()
Try in Editor →

That's it. Three participants, two synchronous calls, rendered with proper lifelines. No participant declarations required.

Explicit Participants (for ordering)

If you need a specific left-to-right order, declare participants upfront:

zenuml
// Declare for ordering
Client
OrderService
PaymentGateway
InventoryService

Client.OrderService.createOrder()
OrderService.PaymentGateway.charge()
OrderService.InventoryService.reserveStock()
Try in Editor →

Message Types: Sync, Async, Creation, and Reply

Sync Message (Blocking)

The default — caller waits for response:

zenuml
Client.AuthService.login(username, password)
AuthService.Database.queryUser(username)
Try in Editor →

Async Message (Fire and Forget)

Use the async keyword before the call:

zenuml
Client.emailService.sendWelcomeEmail()
async emailService.SMTP.dispatch()
Try in Editor →

Object Creation

new creates a new participant mid-diagram:

zenuml
Client.OrderService.createOrder()
new Order(id)
Order.InventoryService.reserveStock()
Try in Editor →

Reply Messages

Use return to show a response flowing back:

zenuml
Client.AuthService.authenticate(token)
return userSession
Try in Editor →

Control Structures: Loops, Conditionals, Parallel

Loops

ZenUML supports while, for, forEach, and loop:

zenuml
Client.PaymentGateway.processPayment()
while(paymentPending) {
  PaymentGateway.Bank.checkStatus()
  Bank.PaymentGateway.statusResponse()
  // Retry until resolved
}
Try in Editor →

Conditionals (Alt/Opt)

if/else if/else maps to UML alt fragments. opt is a standalone conditional block:

zenuml
Client.OrderService.placeOrder()
if(order.total > 500) {
  OrderService.FraudCheck.verify()
} else {
  OrderService.PaymentGateway.charge()
}
opt(sendEmail) {
  OrderService.EmailService.sendConfirmation()
}
Try in Editor →

Parallel Execution

par shows concurrent actions:

zenuml
Client.API.createAccount()
par {
  API.EmailService.sendWelcomeEmail()
  API.AuditService.logEvent()
  API.Analytics.trackSignup()
}
Try in Editor →

Try/Catch/Finally

For exception handling flows (maps to UML break):

zenuml
Client.PaymentGateway.charge(amount)
try {
  PaymentGateway.Bank.authorize()
  return success
} catch {
  PaymentGateway.Client.declineMessage()
} finally {
  PaymentGateway.AuditService.logTransaction()
}
Try in Editor →

Real-World Example: E-Commerce Checkout API

Here is a complete checkout flow — authentication, order creation, payment, inventory, notification, all in one diagram:

zenuml
// Participants
Customer
APIGateway
AuthService
OrderService
PaymentGateway
InventoryService
EmailService

Customer.APIGateway.checkout(cartId)
APIGateway.AuthService.validateToken()
return userContext

APIGateway.OrderService.createOrder(userContext, cartId)
OrderService.PaymentGateway.charge(paymentDetails)
PaymentGateway.Bank.authorize()
return transactionId

OrderService.InventoryService.reserveStock(cartItems)
if(reserveSuccess) {
  OrderService.InventoryService.commitStock()
} else {
  OrderService.PaymentGateway.refund(transactionId)
  return "Order failed — out of stock"
}

async OrderService.EmailService.sendConfirmation(userEmail, orderId)

return orderSummary
Try in Editor →

That's a complete e-commerce flow — auth, payment, inventory, compensating transaction on failure, async email — all in ~25 lines.

Real-World Example: OAuth 2.0 Authorization Code Flow

zenuml
User
Browser
AppServer
AuthProvider
ResourceServer

User.Browser.clickLogin()
Browser.AppServer.GET /login
Browser.AuthProvider.GET /authorize?client_id=...
AuthProvider.User.displayConsentScreen()
User.AuthProvider.approve()

AuthProvider.Browser.redirect(code=xyz)
Browser.AppServer.GET /callback?code=xyz
AppServer.AuthProvider.POST /token
return {access_token, refresh_token}
AppServer.ResourceServer.GET /user
return userProfile
AppServer.Browser.homePage(userProfile)
Try in Editor →

Comments and Documentation

Inline // comments render above the message they're attached to:

zenuml
Client.AuthService.login(username, password)
// Validate credentials against user database
AuthService.Database.queryUser(username)
if(!valid) {
  // Rate limit after 5 failed attempts
  return "401 Unauthorized"
}
Try in Editor →

Aliases: Short IDs, Long Labels

Use as to give participants readable labels while keeping the diagram source compact:

zenuml
Client
PG as "Payment Gateway (Stripe)"
IS as "Inventory Service"

Client.PG.charge(100)
PG.IS.reserveStock()
Try in Editor →

Annotators: Icons and Roles

Add visual roles to participants with annotators:

zenuml
@Actor User
@Database DB
@Boundary APIGateway
@Control OrderService
@Entity Order

User.APIGateway.placeOrder()
APIGateway.OrderService.createOrder()
OrderService.DB.insert(Order)
Try in Editor →

Available annotators: @Actor, @Database, @Boundary, @Control, @Entity, @EC2, @S3, @Lambda, and more.

ZenUML vs Classic Mermaid Sequence: When to Use Which

Use ZenUML when:

- Your team already thinks in code — it maps directly to method calls

- You want simpler syntax with automatic lifeline management

- You're documenting API flows, microservice interactions, or OAuth flows

- You prefer if/while/for/par/try over alt/loop/par/break

Use classic Mermaid sequence when:

- You need fine-grained activation/deactivation control

- You want explicit numbered steps

- You need advanced features like notes over multiple participants, critical regions, or parallel lifelines with overlap

Both render to the same visual output — choose the syntax that matches your brain and your team's comfort zone.

Quick Reference Cheat Sheet

// Sync call
A.B.method()

// Async
async A.B.send()

// Create
new Order(id)

// Reply
return result

// Loop
while(x) { }
for(i=0; i<10; i++) { }
forEach(item in items) { }
loop { }

// Conditional
if(x) { } else if(y) { } else { }
opt(condition) { }

// Parallel
par { A.B.m1(); A.C.m2(); }

// Exception
try { } catch { } finally { }

// Comments
// This renders above the next message

// Aliases
PG as "Payment Gateway"

// Annotators
@Actor User
@Database DB

---

ZenUML brings sequence diagrams closer to the code they describe. When your API docs, system design docs, and architecture diagrams all speak the same pseudocode language, teams spend less time translating and more time building.