Embedding PlantUML in Hexo (server vs client rendering)

puml.online

Two paths to embed PlantUML in a static blog: pre-render on the server, or have the reader’s browser render on the fly. Here’s the field guide.

Option A: Server-side render (hexo-renderer-plantuml)

Workflow: during hexo generate, the plugin replaces ```plantuml blocks with rendered SVG. Final HTML already contains the diagram; no client JS needed.

1
2
# install
npm install hexo-renderer-plantuml

Configure _config.yml:

1
2
3
4
5
plantuml:
render: server # or 'local' (local Java + plantuml.jar)
server: https://www.plantuml.com/plantuml
inline: false
syntax: plantuml

Write the post:

1
2
3
4
5
6
7
8
```plantuml
@startuml
participant FE
participant API
FE -> API: POST /login
API --> FE: 200
@enduml
```

hexo generate calls plantuml.com for the SVG; the plugin writes it into the page:

1
2
3
<svg xmlns="..." viewBox="...">
<!-- rendered diagram -->
</svg>

Pros

  • Article HTML already contains the SVG; works with JS disabled
  • SEO-friendly — crawlers can index the diagram
  • Deploys to any static host (GH Pages, Gitee Pages, Cloudflare Pages)

Cons

  • Build-time network requests to plantuml.com; flaky network = flaky builds
  • Editing a diagram requires a full hexo generate
  • Switching to a self-hosted renderer needs Java in your CI (render: local)

Local render (render: local)

To keep your docs off plantuml.com’s radar, install Java:

1
2
apt install default-jre
# or Adoptium Temurin
1
2
3
plantuml:
render: local
plantumlJar: /usr/local/plantuml/plantuml.jar

hexo generate invokes Java to produce SVGs — entirely offline.

Option B: Client-side WASM render (TeaVM)

Workflow: the plugin emits <pre class="plantuml"> blocks. The browser runs the TeaVM-compiled plantuml.js to render each block into an SVG at view time.

Use a plain fenced code block:

1
2
3
4
5
6
7
8
```plantuml
@startuml
participant FE
participant API
FE -> API: POST /login
API --> FE: 200
@enduml
```

Drop the script into the theme layout:

1
2
3
4
5
6
7
8
<script src="/vendor/plantuml.js"></script>
<script>
document.querySelectorAll('pre.plantuml').forEach(p => {
const src = p.textContent;
plantuml.render(src.split('\n'), p.id, {});
p.id = '';
});
</script>

Pros

  • Zero backend dependency — CI just runs hexo
  • Edit → refresh → see new diagram; fast iteration loop
  • Articles continue to render even when the upstream renderer is down

Cons

  • First page-load pulls ~7 MB of plantuml.js; costly on mobile
  • TeaVM-compiled WASM has known bugs on class / state / use case / component / object / deployment diagrams
  • No SEO value — crawlers don’t run JS

Option C: Self-hosted PlantUML server (the in-between)

When documentation quality matters more than load speed, and you don’t want to depend on plantuml.com:

1
2
FROM plantuml/plantuml-server
EXPOSE 8080
1
2
3
plantuml:
render: server
server: https://plantuml.yourcompany.internal/plantuml

Run the PlantUML server on the internal network; hexo calls it during the build. External crawlers still won’t see diagrams, but every internal article is a real SVG.

Decision matrix

Scenario Pick
Public blog, infrequent updates Option A (hexo-renderer-plantuml)
Frequent updates, internal docs, technical blog new project Option B (client WASM)
Large team, internal-only, needs self-hosting Option C (self-hosted server)
Don’t want to set up Java or any backend Option B
Articles go to PDF / email / WeChat Option A (pre-rendered SVG)

How puml.online does it

This site is puml.online. The root (puml.online) is the blog entry point; the standalone /editor/ is the WASM editor.

  • Blog posts: Option Ahexo-renderer-plantuml calls an internal API during hexo generate.
  • Editor: Option B — TeaVM-compiled plantuml.js + viz-global.js vendor under source/editor/vendor/.

The two are isolated: blog posts depend on no client scripts; the editor is an interactive real-time engine. Cleanest dual-stack.

Common pitfalls

  • JS module orderingvendor/plantuml.js is an ES module. Load it with <script type="module"> or use the TeaVM-compiled IIFE, never mix.
  • TeaVM fails on class / usecase / state diagrams — the compiled WASM has a known bug at $jsException. Either switch to Option A for those diagrams, or rewrite them as sequence + activity equivalents.
  • plantuml.com flakiness — public service often 502s from China. Self-host in production, or switch to render: local.
  • Trailing whitespace / blank lines around @enduml — the renderer is strict. Trim before publishing; if a sample renders in the editor but fails in the post, that’s where to look first.
  • Title: Embedding PlantUML in Hexo (server vs client rendering)
  • Author: puml.online
  • Created at : 2026-07-29 14:25:00
  • Updated at : 2026-08-14 21:34:29
  • Link: https://puml.online/blog/plantuml-render-from-hexo-en/
  • License: This work is licensed under CC BY-NC-SA 4.0.