Render ```plantuml code blocks in the built-in Markdown preview — no Java, no server, no network connection.
Your diagram source is processed locally and is not sent to a rendering service.
Built for design docs you can't send anywhere — write, preview, done.
Status: Active (best-effort maintenance)
- Features
- Installation
- Quick Start
- Why PlantUML Local
- Usage
- Known Limitations
- Configuration
- How It Works
- Security and Privacy
- Platform Requirements
- Troubleshooting
- Contributing
- Support & Maintenance Policy
- License
- Acknowledgments
- Built-in Preview: Diagrams appear in the same Markdown preview you already use (
Ctrl+Shift+V) - Offline Rendering: No Java, no PlantUML server, no network connection required — nothing to install besides the extension
- Fault-Tolerant: A syntax error shows up inline at the broken diagram; the rest of the page stays intact
- Multi-Diagram Pages: Any number of diagrams per page; renders are serialised so results never mix
- Dark-Mode Aware: Diagrams re-render to match your colour theme, or pin the palette via settings
- Full-Width Text Support: Japanese and other full-width characters are measured and laid out correctly
- Non-Intrusive: All other fenced code blocks (
```js,```mermaid, …) are left untouched - Local by Design: Rendering is isolated in a worker thread; only sanitised SVG reaches the preview
- Open the Extensions view (
Ctrl+Shift+X) - Search for PlantUML Local
- Click Install
You can also open the Marketplace page directly:
If you just want to use PlantUML Local, installing from the Marketplace is the easiest option.
git clone https://github.com/kkdev92/plantuml-local.git
cd plantuml-local
npm install
npm run install-local-
Open any Markdown file
-
Add a fenced code block with the
plantumllanguage:```plantuml @startuml Alice -> Bob : Hello @enduml ```
-
Open the preview (
Ctrl+Shift+V) -
The block renders as a diagram — edit and save, and it follows
See sample.md for a tour of diagram types, including error handling.
Previewing PlantUML in VS Code usually means one of two things: installing a Java
runtime to run plantuml.jar locally, or handing your diagram source to a
PlantUML server. The server route is often the public one at plantuml.com,
which means the source of every diagram is encoded into a URL and sent out on
each preview — not always acceptable for confidential or internal design
documents.
PlantUML Local takes a third route: it renders ```plantuml blocks on
your own machine and inserts the resulting SVG into VS Code's built-in Markdown
preview.
- No Java runtime
- No PlantUML server
- No separate preview panel
- No network connection required to render
The extension bundles the official
@plantuml/core JavaScript
build of PlantUML; Graphviz layout is provided locally by Viz.js compiled to
WebAssembly.
PlantUML Local renders most diagram types available in the bundled PlantUML browser engine, including sequence, use-case, class, state, activity and component diagrams.
The first render after startup is the slow one — around 0.4 s on a typical development machine while the WebAssembly engine initialises. Later renders are considerably faster, though timings depend on the machine and on how complex the diagram is.
If a diagram looks stale, run PlantUML Local: Clear Render Cache and Re-render
from the Command Palette.
Sprites render, including the Azure icon set, which ships inside the extension:
```plantuml
@startuml
!include <azure/AzureCommon>
!include <azure/Compute/AzureFunction>
!include <azure/Databases/AzureCosmosDb>
AzureFunction(fn, "Orders API", "Functions")
AzureCosmosDb(db, "Orders", "Cosmos DB")
fn --> db
@enduml
```The library is Azure-PlantUML
and the include paths are its own, so existing diagrams work unchanged — and,
as everywhere else here, nothing is downloaded to render them. Sprites written
directly into the diagram with sprite $name […] { … } work too.
Azure is the only icon set bundled. The AWS and GCP libraries both place their
icons under CC-BY-ND 2.0 with only the macros under MIT, and this extension
ships nothing but MIT / BSD / EPL; Azure-PlantUML has no such split. Other
!include <…> libraries report that they are unavailable rather than being
fetched — but their sprite definitions can be pasted into the diagram, which
renders identically.
The preview is the only place a ```plantuml block becomes a diagram — GitHub
renders one as source, not as a picture. To be readable there too, export the
SVG and reference it. One command does both. Name the block:
```plantuml orders-api
@startuml
Alice -> Bob : Hello
@enduml
```Export All Diagrams and Update References then writes
images/orders-api.svg and, directly below the block, a Markdown image
reference whose target is images/orders-api.svg#plantuml-local.
| Command | What it does |
|---|---|
PlantUML Local: Export Diagram as SVG |
Writes the block under the cursor |
PlantUML Local: Export All Diagrams as SVG |
Writes every named block in the file |
PlantUML Local: Export All Diagrams and Update References |
The above, then inserts or updates the image line after each block |
All three are also in the editor's right-click menu: the single export appears with the cursor inside a block, the other two whenever the file contains a diagram — so the menu of an ordinary Markdown file stays untouched.
The word after the language — orders-api above — names the output file. It is
what ties a block to its SVG across edits, which a position could not: inserting
a diagram above would silently repoint everything below it. Naming is therefore
required for the bulk commands, and the single-diagram one asks when the block
has none — or when its name could not be a file name, since names are limited
to letters, digits, hyphens and underscores.
The #plantuml-local fragment on the inserted reference does two jobs. GitHub
ignores it and renders the SVG, while this extension's preview hides marked
images — the block above them already renders, and without that the same
diagram would appear twice (plantumlLocal.hideExportedImages turns this off,
for checking how the exported file itself looks). It also marks the line as
machine-managed: renaming a block or changing the export directory rewrites the
line on the next run, while every line without the marker — including a
hand-written reference to the same file — is never touched. The update command
is idempotent: running it twice changes nothing, and one Undo reverts whatever
it wrote.
Exports use the light palette regardless of your editor theme, since the files
face hosts whose background this extension does not control;
plantumlLocal.exportTheme pins dark or follows the preview instead. Each
exported SVG also carries an opaque background of its palette — the engine
leaves the canvas transparent, and a dark host page would otherwise show
through it.
plantumlLocal.exportDirectory (default images) decides where files go,
relative to the Markdown file rather than to the workspace root, so moving a
document keeps its diagrams beside it. Exporting writes files, so it needs a
trusted workspace — the preview does not.
Two things to keep in mind. The SVG is a snapshot: after editing a block,
re-run the command (commit the images/ directory alongside the document,
or the references point at nothing). And this flow is for hosts that do
not render PlantUML — one that does, like GitLab, renders the block itself
and would show the referenced image as a second copy.
- Only the
azurestandard-library entry is bundled; other!include <…>libraries are unavailable !includeof a URL or of a file is not supported: URL-based directives are rejected with an inline message, and file includes are not available in the bundled browser build of the engine- Remote themes, images and other network resources are not supported
- Features excluded from the bundled PlantUML browser build are unavailable
- Text is measured with approximate metrics (Node has no Canvas), so element widths, line wrapping and placement can differ slightly from plantuml.com
- A render that exceeds 30 seconds is terminated
| Setting | Default | Description |
|---|---|---|
plantumlLocal.theme |
auto |
Diagram palette. auto follows the VS Code theme; light / dark pin it |
plantumlLocal.logLevel |
info |
Floor for the PlantUML Local output channel. VS Code's own channel level applies first — see Troubleshooting |
plantumlLocal.exportDirectory |
images |
Where exported SVGs are written, relative to the Markdown file. . for the same folder; absolute paths and .. are rejected |
plantumlLocal.exportTheme |
light |
Palette for exported SVGs. preview follows the palette the preview currently uses |
plantumlLocal.hideExportedImages |
true |
Hide images marked #plantuml-local in the preview, so an exported diagram is not shown next to its block's render |
Rendering inside the preview webview — the way Mermaid extensions work — is not
an option for PlantUML: use-case, class and state layouts come from Graphviz,
which ships as WebAssembly, and the preview's Content-Security-Policy does not
grant wasm-unsafe-eval.
So the engine runs in a worker thread on the extension host (no CSP there), and only the finished, sanitised SVG is injected into the preview. PlantUML Local contributes no scripts to the Markdown preview — the sanitised SVG is the only thing it adds to the rendered document.
markdown-it's fence rule is synchronous while rendering is not; they meet
through a cache: the first pass shows a placeholder and starts rendering,
completion triggers one debounced preview refresh, and the second pass serves
the SVG from cache.
PlantUML Local is designed to render diagrams without sending their source to an external rendering service.
- Local Rendering: The engine, the Graphviz WebAssembly, the bundled icon library and all runtime assets ship inside the VSIX and load from disk
- No Telemetry: The extension collects no usage data and makes no intentional network requests
- Network Guard:
fetch/XMLHttpRequest/WebSocket/EventSourceare replaced with throwing stubs inside the render worker — a network attempt fails the render instead of making a request - Remote References Rejected:
!include https://…/!theme … from https://…render an explanatory message instead of reaching the engine - Worker Isolation: The engine's browser shims live in a worker thread, never on the extension host globals
- Render Timeout: A render exceeding 30 s is abandoned; the worker is terminated and restarted
- SVG Sanitisation: Scripts, event handlers and non-fragment links are stripped before SVG reaches the preview. The one exception is a rasterised sprite, which must reach the preview as an inline
data:image/png— it is allowed on<image>only, must be base64 with no other characters, and must actually begin with the PNG signature - Untrusted Workspaces Supported: No workspace files are read, no processes are spawned
These controls reduce the extension's attack surface, but they have limits worth being explicit about: a worker thread is an isolation boundary for globals, not a process- or OS-level security sandbox, and stubbing browser-style network APIs is not the same as closing every network path available to Node.js.
CI verifies each package: verify-vsix unpacks the VSIX, checks for leaked key
material and rendering-service URLs, and renders a diagram from the packaged
worker.
For the full threat model and for vulnerability reporting, see SECURITY.md.
- VS Code 1.134 or later
- Windows, macOS or Linux, on x64 or ARM64
That's it — no Java runtime, no Graphviz install, no external tools.
Upgrading? The minimum is now 1.134 — it was 1.125 from 0.4.0, and 1.101 before that. Older installations keep the version they have and stop receiving updates.
CI runs the test suite on Windows, macOS and Linux (x64 on Windows and Linux, ARM64 on macOS). The extension is plain JavaScript and WebAssembly, so other combinations are expected to work; please open an issue if one does not.
- Stuck on "Rendering diagram…": Check Output → PlantUML Local for worker errors, then run
PlantUML Local: Clear Render Cache and Re-render - A red syntax-error box appears: The message comes from the PlantUML engine — only that diagram is affected, and the rest of the page still renders
- Colours look wrong after switching themes: Backgrounds follow the palette the diagram was rendered with. If you pinned
plantumlLocal.theme, that palette wins by design - Layout differs from plantuml.com: Text is measured with approximate metrics in the Node renderer, so box widths, line wrapping and element placement can differ slightly
- Setting
logLevel: debugshows nothing new: The channel is aLogOutputChannelnow, and VS Code decides what one of those shows — an extension cannot raise its own channel's level. Run Developer: Set Log Level and pick PlantUML Local; that choice is per channel and survives a restart.plantumlLocal.logLevelis a floor on top of it, so it can only make the log quieter
Contributions are welcome — thank you for helping make PlantUML Local better 🙌 Please see CONTRIBUTING.md for guidelines.
If you're planning a larger change, opening an issue first is appreciated (it helps align direction and avoids duplicate work). Note that features requiring Java, a server or network access are out of scope by design.
PlantUML Local is a personal hobby project maintained in spare time. The project is active, but support is best-effort: I'll do my best to review issues and PRs, and releases may be a bit slow sometimes — thank you for your patience.
Helpful things when reporting bugs:
- OS / architecture / VS Code version
- The smallest PlantUML source that reproduces the issue
- Output from Output → PlantUML Local, with the level set to Debug via Developer: Set Log Level
Security-related reports should follow SECURITY.md. Really appreciate you using PlantUML Local 💛
PlantUML Local is licensed under the MIT License — see LICENSE.
The bundled engine @plantuml/core is MIT-licensed from version 1.2026.6 onwards (earlier versions are GPL-3.0-or-later). This extension therefore depends on ^1.2026.6, which permits any compatible 1.x release from that version up; the exact version a build used is recorded in package-lock.json.
Copyright and licence notices for the third-party code shipped inside the VSIX are collected in THIRD_PARTY_NOTICES.md.
- Diagram rendering powered by PlantUML and its
@plantuml/coreTeaVM build — this extension is a third-party project, not affiliated with or endorsed by the PlantUML project - Graphviz layout by Viz.js, a WebAssembly build of Graphviz shipped inside
@plantuml/core - DOM for the engine by happy-dom
- Extension framework by @kkdev92/vscode-ext-kit
