With PlantUML you can create diagrams as plain text code. PlantUML takes off the burden so that you don’t need to worry about the looks and layout of your diagram’s elements, by applying opinionated defaults. This article is both a PlantUML layout tutorial, which explains how you can customize the layout, and also explains how you can customize the styles, influencing how your elements look.
Introduction
PlantUML is a Java-based tool that lets you express diagrams as code. As my introduction article explains, you can use PlantUML for any kind of UML diagram and beyond, including mind maps or Gantt charts.
While PlantUML does the “right thing” most of the time, sometimes you will need to tweak a few settings. Finding the right keywords is a chore at times, which is why I wrote this article to explain tricks for two of the most often needed customization options: layouting and customizing colors and other styling elements.
PlantUML layout tricks
PlantUML takes care of your diagram layout, so that you don’t have to. Unfortunately, the layout produced by PlantUML is (very) sub-optimal in specific cases, or the arrangement of elements is simply not to your taste. This section presents several tricks you can use to tweak the layout. However, the authors of PlantUML advice you to use those sparingly.
Flow and arrow direction
Your diagram consists of one or more sets of connected elements (that is, the different sets themselves are not connected). For instance, imagine a diagram of six nodes (A, B, C, D, E, F
) with two connected sets, e.g. set #1 somehow connects nodes A
, B
, and C
with each other, and set #2 somehow connects D
, E
, and F
.
For nodes within such a set, there is a default layout direction PlantUML uses, which is configurable. If you don’t configure anything, this is the top to bottom direction
. This is why a statement such as A --> B
produces an arrow going from top to bottom. You can change this default by adding the left to right direction
statement near the top of your file.
Considering the different, disconnected sets, PlantUML will layout them in the direction perpendicular to the default flow direction (so, by default, from left to right), as illustrated by the following example:
@startuml
' The following statement is the default, you could also
' comment it out:
top to bottom direction
' The two following disconnected sets (realized as packages) are arranged left to right
package "Disconnected set #1" {
[A]
[B]
}
package "Disconnected set #2" {
[C]
[D]
}
package "Disconnected set #3" {
[E]
[F]
}
A --> B
C --> D
E --> F
@enduml
Code language: plaintext (plaintext)
There are a few subtle notions about defining connections that you should be aware of, so let’s take a look at the syntax:
A -> B
orA - B
(or any other combination where you use a single character to describe the type of line, not counting the arrow tip character) produces an arrow that goes in the direction perpendicular to the default-configured flow. So by default (top to bottom direction
), the arrows that you get for something likeA - B
go from left to right. If you had usedleft to right direction
, the arrows would go from top to bottom.A --> B
orA -- B
(etc.), i.e. using two or more characters that describe the line type, produces arrows that follow the direction of your main flow exactly. The more characters you add, the longer the arrow will become.- Unfortunately, it is not possible to create longer arrows that go in the perpendicular direction of the main flow.
- A notation such as
A -u- B
enforces the direction of the arrow (u
could be replaced withd
,l
, orr
, or its corresponding long versions:up
,down
,left
orright
). The direction-letter (u
, …) denotes the direction of the arrow itself (in other words: its target)! For instance,A -up- B
means that A is below B!
Flipped directions
If you use the left to right direction
command, then explicitly specified directions (such as A -up- B
) don’t do what you would expect. PlantUML applies such explicitly specified directions in a first stage, where they are still interpreted in the context of the default top to bottom direction
. Only afterwards is the layout (mentally) rotated by 90°. Thus, if you used the left to right direction
, and wanted A
to be below B
, you need to write A -left- B
. If you wanted A
to be left of B
, instead of A -right- B
you’d write A -down- B
.
Invisible links
To force PlantUML to place a specific element above/below/left of/right of some other element, you can use invisible links. These are like normal connections, but are not drawn. The syntax is A -[hidden]left- B
where left
can be exchanged with any other direction, or its shortcut (e.g. l
for left
). The following example illustrates this:
@startuml
[a]
[b]
[c]
[d]
a - b
c - d
a -[hidden]d- c
@enduml
Code language: plaintext (plaintext)
As long as the connection’s direction matches the default layout direction (which is the case in the above example, because d
=down
matches the default top to bottom direction
), additional dashes before [hidden]
or after the direction identifier, d
, will also increase the horizontal/vertical distance.
Together constraints
By wrapping two or more elements in a together {...}
block, you basically create an invisible box around those elements. In other words, no other element will come between those elements.
@startuml
together {
[a]
[b]
}
[c]
a -- b
a -- c
@enduml
Code language: plaintext (plaintext)
You should also try the example with single dash connectors (-
) instead of --
(open example in editor). Observe what happens if you remove the together
constraint.
Ratio pragma
Using !pragma ratio x
where x
is a floating point number, you can influence the width-to-height ratio.
Roughly, x = height/width
.
For instance, !pragma ratio 2.0
will produce an image that is roughly twice as high as it is wide. There are two caveats you should be aware of:
- The ratio is not exact. For instance, the pixel dimensions of a generated image for
!pragma ratio 2.0
will not be exactly twice as high as it is wide – only approximately. - The layout engine will not intelligently rearrange diagram elements to fill up space – it just stretches out your elements, increasing the margin between the elements.
Margins and paddings
For most diagram types (such as deployment, class, …) you can change the margin between elements using skinparam settings:
skinparam nodesep x
(wherex
is an integer > 0) will increase the horizontal marginskinparam ranksep x
affects the vertical margin
For sequence diagrams, however, the above definitions do not work. Here a skinparam ParticipantPadding x
will increase the horizontal distance between actors/participants (your life lines). I am not aware of any mechanism to increase the vertical distance between messages.
With skinparam padding x
(x
also being an integer > 0) you will increase the padding of every kind of element, so use it carefully, with small values.
Customizing colors and PlantUML styles
PlantUML offers three mechanisms to customize colors and other styling-related things, such as margins, sizes, etc. These are:
- Creole/HTML (docs): lets you build tables, bullet lists and trees, and style text placed inside your boxes, e.g. affecting their color, size, style (underline), etc. The docs give a good overview of how this mechanism works.
- Skin parameters (docs): lets you define styling-related settings, most notably font name/color/type/size, background/border color, and a few other settings.
- Style sheets (docs): a new, still work-in-progress approach for achieving the same effects as the skin parameters.
The authors of PlantUML plan to replace skin parameters with the style sheet mechanism in the future. I personally like the style sheet approach better anyway. Let’s take a closer look at these three approaches!
Creole/HTML
You can create (blocks of) text that is part of an element (or connection) which is styled in various ways, influencing its color, size, font, etc. This can be done by using specific HTML-like tags (such as <color:red>
or <b>
) or the so-called Creole syntax, which is a light-weight markup language also used in other systems, slightly extended by PlantUML-specific syntax. You can of course also mix Creole and HTML. The official docs will serve you fine to understand the basics.
Skin parameters
The official docs already hint at the capabilities of skin parameters. Unfortunately, those docs are quite incomplete. There is another external page that lists all skin parameters and provides example images to clarify what each setting means. However, not only is that latter page also outdated (some skin parameters are missing – the list of really all skin parameters is available here), but it misses many example images.
Ultimately, you have to go through the list of skin parameters and see which ones you need, digging through the different sources of documentation. Here are two interesting ones that caught my eye.
First, there is a Handwritten
or monochrome
look, which helps you achieve an “unfinished” kind of look, which is useful for preliminary, work-in-progress diagrams.
@startuml
' Uncomment one (or both) of the following lines
' skinparam handwritten true
' skinparam monochrome true
class Object
class String extends Object
class Date extends Object
@enduml
Code language: plaintext (plaintext)
If you don’t like the default curved routing of connections in more complex diagrams, you can customize the linetype
skinparam with values polyline
or ortho
, to customize how connections are routed:
@startuml
'skinparam linetype polyline
'skinparam linetype ortho
[a]
[b]
[c]
[d]
[e]
a --> e
b --> c
c --> d
c -l-> e
[rdf]
[qq]
b -> rdf
rdf -d-> a
c - qq
qq -d- rdf
@enduml
Code language: plaintext (plaintext)
Style sheets
Style sheets are extremely poorly documented in the official docs. I recommend looking at the plantuml.skin file and the post written on May 5, 2020 in this forum thread, to get started via examples.
Style sheets have a number of advantages over skin parameters:
- Style definition can be placed in a separate css file, referenced via
<style file=MyOwnFile.css>
- Improved readability (over skin parameters), especially for people already familiar with CSS
- There are dynamic selectors (such as the
depth
selector described in the docs) - Easier scoping as shown in the code below:
<style>
' scope to sequenceDiagram elements
sequenceDiagram {
' scope to actor elements
actor {
FontColor Blue
}
}
</style>
Code language: plaintext (plaintext)
Coloring only specific objects
Sometimes you want to apply a certain style only to a specific instance of an element, but not to other instances. For example, you might want a very specific arrow to be bold or have a different color, or want to highlight a few specific classes of your class diagram with a different background color.
To achieve this, you should first skim over the docs of the specific diagram type (e.g. class) that you are building, to find out what kinds of inline styling options there are. For instance, the class diagram docs mention that you can use a notation such as A -- B #line:red;text:red : "link text"
to connect class A
with B
via a red line with red label text. As you can see, such a notation lets you modify a specific element of the diagram by putting the concrete styling inline, right next to it.
However, often the aspect you want to style is not available in such an inline way. For instance, how would you set the background color of a specific class? The trick is to use custom stereotypes for this, which are similar to stereotypes you might know from, say, class diagrams (e.g. <<uses>>)
. The general idea works like this:
- You create a style that only applies to a specific stereotype (whose name you choose). You can use either the skin parameter or the style sheets mechanism
- You assign the stereotype to one or more of your elements in your diagram
- (Optional) you tell PlantUML to hide that stereotype’s label, because you basically “abused” the stereotype mechanism (which assigns semantics to an element) for visual purposes.
The following concrete example illustrates how to perform these steps:
@startuml
' 1. Create the style for our self-named stereotype, "myStyle"
' Either using skin parameters (note there is no space before <<) ...
skinparam ClassBackgroundColor<<myStyle>> LightGreen
' ... or using style sheets (commented out)
/'<style>
.myStyle {
BackgroundColor LightGreen
}
</style>'/
class A {
+int foo
}
' 2. Assign the stereotype to one of your elements
class B <<myStyle>> {
+int bar
}
A - B
' 3. Optionally hide the stereotype
hide <<myStyle>> stereotype
@enduml
Code language: plaintext (plaintext)
Conclusion
PlantUML is a tool that has grown over time and now has a huge amount of features. Not all of them are easy to understand, which is also due to the lacking documentation. While this sounds dire, it is not really a problem in practice. This article provided a lot of hints. With practice these thing will become effortless. I recommend that you build your own cheat sheet for syntax that solves specific problems (e.g. for layouting or colorization), to make you more effective in the long run. I’ve discussed cheat sheets in detail in this article.
Is there a way to connect some structured documentation e.g. json and components in one diagram?
And BTW, any hints on line breaks in the json?
I suppose this highly depends on the generator you would normally apply to that structured documentation (which generates human-readable formats, like HTML). After all, structured documentation is not really consumable for humans, as it is not visually appealing. This kind of generator could be modified to also render PlantUML files.
Line breaks in JSON string fields are just represented with
\n
.When nesting rectangles as used in Archimate for example, the order of the nested set is completely random even with explicit connectors between them. Your advice didn’t help unfortunately. A shame because I’d really like to use PlantUML for Archimate. May raise a feature request.
Hello,
is there any way to align a Json visualisation more left to right than top to bottom?
My problem is that I get 1’500×8000 pixel output, but where 1 3000×4000 would probably be sufficient with a more sideways oriented layout.
I would even accept a long column from top to bottom to be wrapped to two columns side by side, and simply linked together with a line.