PlantUML preprocessor directives & icon libraries
When 10 PlantUML diagrams share the same skin and palette, preprocessor directives let you extract common code. When you want intuitive icons for docs, the icon libraries do that — together they take PlantUML from “draw each one” to “template-driven.”
Preprocessor cheat sheet
Directive
Use
!include <path>
Inline an external PlantUML file / text
!includeurl <url>
Inline from URL
!include_once
Include only the first time
!define <VAR> <value>
Define a preprocessor variable
!function <name>(args)
Define a preprocessor function
%variable_name
Reference a preprocessor variable
%date("format")
Current date
%dirpath(...) / %filename(...)
Filename helpers
!if / !else / !endif
Preprocessor conditions
!definelong
Multi-line definition
%load_json / %load_yaml
Read external data
1. !include (most common) Extract shared styles:
1 2 3 4 5 6 # source/plantuml/_theme.puml !define THEME_DARK skinparam backgroundColor #FAFAFA skinparam BorderColor #2F4858 skinparam ArrowColor #2F4858 skinparam FontColor #1F2328
Main file:
1 2 3 4 5 6 7 8 @startuml !include /path/to/_theme.puml class Order class Product Order --> Product @enduml
!include inlines content at preprocessing time.
Internal-network note : enterprise networks often block GitHub raw; vendor locally.
C4 inclusion 1 2 3 4 5 6 7 8 9 10 @startuml !include ../plantuml/c4/C4_Container.puml Person(user, "User") Container(web, "Web", "React") Container(api, "API", "Go") Rel(user, web) Rel(web, api) @enduml
Path separators 1 2 !include ../shared/styles.puml !include ../../c4/C4_Container.puml
2. !define / %variable 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @startuml !define COLOR_PRIMARY #A31F34 !define COLOR_SUCCESS #20A464 !define COLOR_WARNING #F4B40A skinparam class { BackgroundColor %COLOR_PRIMARY FontColor white } class A class B A -> B @enduml
!define declares; %NAME references.
Multi-line !definelong 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @startuml !definelong STYLE_CLASS_PRIMARY skinparam class { BackgroundColor #A31F34 FontColor white BorderColor white } !enddefinelong STYLE_CLASS_PRIMARY class A class B @enduml
3. !function 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @startuml !function $accent($type) !if ($type == "primary") !return #A31F34 !else !return #6B7280 !endif !endfunction class A A -> B : %accent("primary") A -> C : %accent("warning") @enduml
Real: status colors 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 @startuml !function $status_color($s) !if ($s == "ok") !return #20A464 !elseif ($s == "warn") !return #F4B40A !elseif ($s == "fail") !return #DC2626 !else !return #6B7280 !endif !endfunction skinparam class { BackgroundColor $status_color("ok") } class Healthy @enduml
4. %date / %time 1 2 3 @startuml title Report generated at %date("yyyy-MM-dd HH:mm") @enduml
Java SimpleDateFormat patterns:
Pattern
Output
yyyy-MM-dd
2026-07-29
yyyy
2026
MM/dd/yy
07/29/26
dd MMM yyyy
29 Jul 2026
Title with date 1 2 3 4 5 6 7 8 @startuml title System status - %date("yyyy-MM-dd") class Service1 class Service2 class Service3 @enduml
5. !if / !endif 1 2 3 4 5 6 7 8 9 10 11 12 13 @startuml !define DEBUG true !if (DEBUG == true) skinparam class { BackgroundColor #F4B40A BorderColor #DC2626 } !endif class Order @enduml
Multi-branch 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 @startuml !define THEME "dark" !if (THEME == "dark") skinparam BackgroundColor #161B22 skinparam FontColor #E6EDF3 !elseif (THEME == "light") skinparam BackgroundColor #FAFAFA skinparam FontColor #1F2328 !else skinparam BackgroundColor white skinparam FontColor black !endif class Order @enduml
Switch theme via CLI 1 2 plantuml -DTHEME=dark *.puml plantuml -DTHEME=light *.puml
6. %load_json / %load_yaml 1 2 3 @startjson %load_json("data.json") @endjson
External data becomes input:
1 2 3 4 5 cat <<EOF | java -jar plantuml.jar -pipe >out.svg @startjson %load_json("$1") @endjson EOF
Icon libraries PlantUML supports standard open-source icon libraries for richer diagrams.
OpenIconic 1 2 3 4 5 6 @startuml class Application <<&person>> database Database <<&cylinder>> @enduml
<<&name>> references OpenIconic. Common:
Code
Icon
<&person>
user
<&cog>
gear / settings
<&lock-locked>
lock
<&check>
check
<&x>
cross
<&bell>
bell
<&calendar>
calendar
<&graph>
chart
<&database>
database
<&cloud>
cloud
<&bolt>
bolt
Built-in stereotypes 1 2 3 4 5 6 7 8 9 10 @startuml class Server <<&server>> class Database <<&database>> class Queue <<&queue>> class Network <<&network>> class Storage <<&storage>> class User <<&user>> @enduml
FontAwesome / Material / AWS 1 2 3 4 5 6 7 8 9 10 11 12 @startuml !include <material/common> !include <material/server> !include <material/database> node "API Gateway" as gw <<$material/server>> database "Postgres" as db <<$material/database>> gw --> db @enduml
Material Icon library 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 @startuml !include <material/common> !include <material/server> !include <material/database> !include <material/globe_network> !include <material/api> class User <<$material/account>> class API <<$material/api>> class Server <<$material/server>> User --> API API --> Server @enduml
AWS / Azure / GCP 1 2 3 4 5 6 7 8 9 10 11 12 13 14 @startuml !include <aws/common> !include <aws/Compute/EC2> !include <aws/Database/RDS> !include <aws/Networking/ELB> class WebServer <<$aws_compute_ec2>> class DB <<$aws_database_rds>> class LoadBalancer <<$aws_networking_elb>> WebServer --> DB LoadBalancer --> WebServer @enduml
Real: full cloud architecture 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 @startuml !include <aws/common> !include <aws/Compute/EC2> !include <aws/Database/RDS> !include <aws/Networking/ELB> !include <aws/Storage/S3> !include <aws/Security/Cognito> actor User <<$material/account>> cloud "VPC" { LoadBalancer <<$aws_networking_elb>> EC2 <<$aws_compute_ec2>> RDS <<$aws_database_rds>> } S3 <<$aws_storage_s3>> Cognito <<$aws_security_cognito>> User --> Cognito : auth User --> LoadBalancer : HTTPS LoadBalancer --> EC2 : HTTP EC2 --> RDS : SQL EC2 --> S3 : Static assets @enduml
Custom stereotypes 1 2 3 4 5 6 7 8 @startuml skinparam stereotypeCBackgroundColor #FAFAFA class OrderService <<(Q, #A31F34) Service>> class PaymentService <<(P, #20A464) Service>> class UserController <<(C, #F4B40A) Controller>> @enduml
<<(X, #color) Label>> defines custom color + label.
Real-world: template-driven diagrams 1 2 3 4 5 6 7 8 9 # _include/puml/style.puml !define COLOR_PRIMARY #A31F34 !define COLOR_BG #FAFAFA skinparam { BackgroundColor %COLOR_BG BorderColor %COLOR_PRIMARY ArrowColor %COLOR_PRIMARY }
1 2 # _include/puml/icons.puml !include <material/common>
1 2 3 4 5 6 7 8 9 10 @startuml !include _include/puml/style.puml !include _include/puml/icons.puml class OrderService <<$material/server>> class PaymentService <<$material/server>> OrderService --> PaymentService @enduml
CI render:
1 2 3 4 - name: Render diagrams run: | # !include relative paths: cd to source/ find source -name '*.puml' | xargs -I {} java -jar plantuml.jar -tpng {}
Anti-patterns 1. Complex preprocessor functions 1 2 3 4 5 6 7 8 9 !function $calc($a, $b) !if ($a > 100 && $b > 50) !return $a * $b + 100 !elseif ($a < 0) !return 0 !else !return $a + $b !endif !endfunction
When logic gets long it’s hard to debug. Keep functions tiny — color picks, text replacements — and use !define for everything else.
2. !includeurl from GitHub 1 !includeurl https://raw.githubusercontent.com/.../C4_Container.puml
CI often fails on flaky network. Vendor locally.
3. Icon overload 1 2 3 4 5 6 7 8 9 10 11 class A class B class C class D class E <<$material/server>> <<$material/database>> <<$material/api>> <<$material/globe_network>> <<$material/storage>> @enduml
When icon library runs out of variety, icons repeat. Worse than no icons.
Review checklist Preprocessor
Icons
TL;DR Preprocessor + icon libraries lift PlantUML from “draw each one” to “template-driven.” Extract common styles + pick an icon library + write a render script, and your entire doc deck renders in one pass.