Just run puml-for-markdown in any directory where you have markdown files and it will render links to PlantUML files
in markdown comments. The best part about this is that it requires no build files, you don't even need to store
puml images in your repo. Also the embedded diagrams can link to other diagrams. PlantUML files
allow you to create diagrams that are defined using a simple and intuitive language. By default the entire directory
is recursively searched, respecting root and nested .gitignore rules, including negation. .git, node_modules, the output directory, and symbolic links are always skipped. Both .puml and .plantuml diagram files are supported.
The following examples are of a project I worked on, yellow components will link you to another diagram. Obviously the diagram content has been obfuscated.
You can easily add an embedded diagram to your markdown. Simply add a link to the puml file in a markdown comment like so:
<!---->
Here's the result. Try clicking on the yellow blocks.
To render a link to a puml diagram, do the same thing but exclude the !
<!--[Click to Open Interactive Diagram](./puml/level_1_system_view.puml)-->
which renders into
Click to Open Interactive Diagram
The goal is to make PlantUML diagrams easily accessible from markdown, specifically GitHub flavored.
- Should work with both private and public repositories
- Should not have to use tokens to get it to work with private repositories
- Should support hyperlinking to other diagrams
- Should support PlantUML
!include - Should support PlantUML sprites (small graphic images)
- Any time you make changes to diagrams you should be able to run the CLI tool to update the markdown links
PlantUML allows you to create diagrams that are defined using a simple and intuitive language. PlantUML diagrams are great for designing new projects but they don't work very well in Github markdown preview. There are some workarounds, but I found these to be unstable and they have a lot of caveats.
Update: Github released support for embeddable Mermaid diagrams, but PlantUML is still unsupported, see here for more info.
Whenever you run the CLI it will add a tinyurl link to the rendered SVG next to the markdown comments referencing a puml diagram. E.g.
<!--[Example With Only Link](./puml/level_1_system_view.puml)-->
will be replaced with
[Example With Only Link](https://tinyurl.com/yfpclfpp)<!--[Example With Only Link](./puml/level_1_system_view.puml)-->
When ! is included in front of the markdown link, it will render the diagram image in the markdown. If the image is
clicked it will open up the diagram. E.g.
<!---->
will be replaced with
[](https://tinyurl.com/yfpclfpp)<!---->
If you want to update the link text or switch it between image and link, just update the comment and rerun the CLI. You don't need to delete the rendered image or link.
Requires Node.js 22 or newer (Node.js 24 recommended for development).
Version 2 raises the minimum Node.js version to 22. Upgrade Node.js before upgrading
from version 1. Existing dependencies pinned to 1.x or ^1.2.1 stay on version 1;
installing latest selects version 2. See the
release notes for the changes.
npm i -g puml-for-markdown
Keep diagrams current without running the CLI yourself. To regenerate and commit them on every push to main:
# .github/workflows/diagrams.yml
name: Diagrams
on:
push:
branches: [main]
permissions:
contents: write
jobs:
diagrams:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: danielyaa5/puml-for-markdown@v2To fail pull requests whose diagrams are out of date instead:
name: Diagrams
on: pull_request
jobs:
diagrams:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- uses: danielyaa5/puml-for-markdown@v2
with:
mode: check| Input | Default | Description |
|---|---|---|
mode |
update |
update regenerates links and images, then commits and pushes them. check regenerates and fails if anything differs from what is committed. |
args |
Extra CLI options, such as --local-images -f svg or -s https://uml.example.com/plantuml. Use the options you run locally, or check will report differences. |
|
root-directory |
. |
Project directory to scan. |
commit |
true |
In update mode, commit and push the changed files. Needs permissions: contents: write. |
commit-message |
Update PlantUML diagrams |
Commit message for update mode. |
cache |
true |
Restore .puml-cache.json from earlier runs, and save it in update mode, so unchanged diagrams are not rendered or shortened again. |
Outputs: changed (true or false), files (changed paths, one per line), and commit (the pushed SHA).
- The action commits only the files the CLI changed, never other changes made earlier in the job, and never
.puml-cache.json. - Commits pushed with the default
GITHUB_TOKENdo not start new workflow runs, so updates cannot loop. updateneeds a branch checkout. Onpull_requestevents usecheck, which also works for forks, or check out${{ github.head_ref }}for pull requests from the same repository.checkcontacts the same renderer and shortener as a normal run. TinyURL returns the same link for the same diagram, so a pull request regenerated locally passes. With-tand no image export,checkneeds no network.- For Polr, pass the key from a secret:
env: POLR_APIKEY: ${{ secrets.POLR_APIKEY }}. - The action uses the runner's Node.js when it is version 22 or newer, and otherwise installs Node.js 22. Linux, macOS, and Windows runners are supported.
@v2follows the latest 2.x release. Pin a release such as@2.1.0, or a commit SHA, to control updates.
Here are some other features supported
- You can specify the CLI to output the diagram images as png and/or svg see
--output-imagesand--image-formats. For historical examples of exported images, see dist_puml/puml. The current pre-commit hook runs the automated tests. - You can specify the PUML server used to render the diagrams see
--puml-server-url
Usage: puml-for-markdown [options]
An application to add interactive PUML diagrams to your github markdown files. If running with default arguments, run in project root directory.
Options:
-s, --puml-server-url <url> This is the base URL used to render
diagrams. Defaults to the public plantuml
server. (default:
"https://www.plantuml.com/plantuml")
-x, --root-directory <path> The path to your project (default: CWD)
-r, --hot-reload Rerun markdown generator every `interval`
seconds, determined by interval option
-v, --interval-seconds <number> If --hot-reload is set, how often should it
reload (default: 2)
-p, --puml-directory <path> Path to directory containing puml files
which are referenced in markdown files
(default: rootDirectory)
-m, --markdown-directory <path> Path to directory containing markdown files
referencing puml files (default:
rootDirectory)
-g, --ignore-gitignore Don't use .gitignore rules to skip PUML and
MD
-i, --gitignore-path <path> Use this as path to .gitignore file.
(default: rootDirectory/.gitignore)
-d, --output-images If set, will output images of diagrams to
the dist directory
-b, --dist-directory <path> If --output-images is set, path to output
diagram images (default:
rootDirectory/dist_puml)
-f, --image-formats <format> If --output-images is set, sets the output
image format (choices: "png", "svg", "both",
default: "png")
-t, --turn-off-link-shortening Use the full puml server link instead of the
tiny url, if your diagrams are too big this
won't work
--local-images Link Markdown to exported local images
(implies --output-images)
--interactive-links Wrap local image previews in an interactive
renderer link
--check Check for stale Markdown and images without
writing or using the network
--dry-run List planned changes without writing or
using the network
--no-cache Disable the persistent diagram and image
cache
-l, --polr-url <url> Use a private Polr shortener; reads
POLR_APIKEY from the environment
-h, --help display help for command
- Comments within inline or multiline code styling will be ignored
- Cyclic diagram links and recursive local includes fail with an error showing the reference chain.
- See the pre-commit hook to see how to add a git hook
- Image export skips files without an
@start...directive and files used only as local includes. A Markdown reference or diagram hyperlink makes an included file exportable. Other files must render successfully. - Anything inside
inline codeorcode blockswill be excluded from rendering
You can use PlantUML using the online web service to generate images on-the-fly. A online demonstration is available at http://www.plantuml.com/plantuml. You can pass the encoded text of your diagrams to the web service in the url path and it will generate an SVG or PNG for you. Here's a simple HelloWorld example http://www.plantuml.com/plantuml/uml/Aov9B2hXil98pSd9LoZFByf9iUOgBial0000. Large diagrams will have very long encoding strings, they can exceed maximum url length. They also don't look very good in markdown files. By default the CLI will use the tinyurl.com service to shorten the link to the diagram.
The CLI will use the plantuml-encoder package to encode puml files. To support hyperlinking diagrams we need to parse
all hyperlinks in the puml files. A dependency graph is created for the files and a DFS is performed where we create
links for the leaf nodes first, then replace the links in the parent nodes with the links to the leaf nodes. The puml
files are not actually modified, only the puml files content in memory is modified. By default the tinyurl free service
is used to shorten the links.
In order to support !include we parse puml files and replace any !include with the contents of the file referenced.
The markdown files are then parsed for markdown comments. If the comments reference a PlantUML file, a link to the web service url will be added next to the comment. Because these links contain the full PlantUML diagram encoding in it, the source can be recovered from the URL. A private repository does not make the renderer or shortener private; see diagram privacy.
Run npm test to execute the automated CLI regression tests. CI runs on Linux,
macOS, and Windows with Node.js 22 and 24. The suite covers Markdown updates,
path handling, ignore rules, includes, image exports, caching, and shortening.
See CONTRIBUTING.md
for development instructions.
- C4-Puml: A collection of PlantUML diagrams for use in C4, you'll see me using this in my examples
Version 2 fixes the reported Windows path lookup issue (#6). Windows path and ignore-rule regressions are covered by automated tests. The version 2 changes were also tested on Windows 11 ARM64 with Node.js 24.
For remaining limitations, see reliability and operational notes.
- Fatal errors return a nonzero exit status, suitable for CI. Watch mode logs errors and retries on the next interval; it runs immediately on startup.
- Local
!includefiles are expanded recursively relative to the file containing the directive. Remote, standard-library, and macro-based includes remain for the renderer to evaluate. This is not a complete PlantUML preprocessor: conditional includes and!include_once/!include_manyare not expanded locally. - Unrelated Markdown links, fenced/indented code, inline code, and line endings are preserved. Unchanged Markdown files are not rewritten.
- All Markdown references are checked before documents are written. Image exports complete before Markdown is updated, but an export failure can leave earlier successfully exported images updated; the operation is not a multi-file transaction.
- Export overwrites the matching generated image files but never clears the output directory. Stale images are retained. Ambiguous output names and symlinks inside the output path are rejected.
- Renderer and shortener requests time out after 30 seconds. If TinyURL fails, a warning is printed and the direct PlantUML URL is used. Large URLs can still exceed renderer/proxy limits; shortening does not remove the renderer's limit.
- Diagram privacy: encoded diagram URLs contain recoverable source text.
The default shortener receives that URL, and the renderer receives the diagram
when someone opens it or when images are exported. For private diagrams, use
your own renderer with
--puml-server-urland disable shortening with-t.
puml-for-markdown --local-images --image-formats svgThis implies --output-images and produces relative Markdown links to files in
dist_puml. For example, <!----> becomes
<!---->.
Paths are relative to each Markdown file and URL-encoded, including spaces.
Both .puml and .plantuml sources work.
SVG diagrams link to other exported SVG diagrams using relative paths. Open the
SVG directly in a browser or serve the export folder to navigate between them.
Markdown hosts may disable links inside embedded SVG images. PNG previews cannot
contain clickable elements. With --image-formats both, Markdown uses SVG.
Add --interactive-links to wrap a local preview in a link to the online renderer.
That renderer link uses separate source with online diagram hyperlinks, while
exported SVGs retain their local hyperlinks. Local-only mode skips URL shortening.
Rendering still contacts the configured PlantUML server; use --puml-server-url
with your own renderer when the source must remain private.
puml-for-markdown --dry-run -t
puml-for-markdown --check -t
puml-for-markdown --local-images --image-formats svg --checkBoth commands are offline and never write Markdown, images, or cache files.
--dry-run lists planned changes and exits successfully; --check exits with 1
if documentation is stale, otherwise 0. Invalid inputs still exit with 1.
Neither can be combined with --hot-reload.
A check that uses shortened URLs or exported images needs the cache from a prior
successful generation with the same settings. If it is missing, the check reports
that generation is needed rather than contacting a shortener or claiming the files
are current. Direct-URL Markdown checks (--check -t without image export) do not
require a cache. For CI, use that deterministic mode or restore the generation
cache with the exported images.
Successful runs save .puml-cache.json at the project root. Add it to your
.gitignore: encoded URLs contain recoverable source text, even for private diagrams.
The cache contains no Polr API key. Renderer/provider settings and a hash of the key
separate cache entries when configuration changes.
The CLI reuses encodings, shortened URLs, and verified image files. Watch mode checks inputs each interval and only rebuilds affected content. Local include changes invalidate their parent diagrams; remote diagram links invalidate parents when their target URL changes. Local links stay stable when only a child changes. Missing or externally modified images are rendered again.
puml-for-markdown --hot-reload
puml-for-markdown --no-cache--no-cache forces regeneration without reading or writing the cache. Use it to
refresh remote includes, renderer upgrades at the same URL, or expired short links:
those external changes cannot be detected from local file content. Failed shortening
attempts are not cached, allowing the next run to retry. Stale generated images are
retained; the CLI never clears the destination directory.
# Set POLR_APIKEY in your shell or CI secret environment first.
puml-for-markdown --puml-server-url https://uml.example.com/plantuml \
--polr-url https://short.example.comThis adapts the provider idea from PR #8
by @mzagar. The implementation uses the Polr v2 API:
POST /api/v2/action/shorten, with URL-encoded url, key, and response_type
fields. JSON and plain-text responses are accepted and validated. Credentials are
never written into generated links or the cache. A failed Polr request falls back
to the direct renderer URL, never TinyURL. --polr-url cannot be combined with -t.
To avoid a shortener entirely, use your own renderer with -t. This is also the
simplest setup for private diagrams when URLs are short enough for your renderer.