DEV Community

Cover image for I Built an Open-Source Mermaid Plugin for IntelliJ — Here's Why and What I Learned
AlexTDev
AlexTDev

Posted on

I Built an Open-Source Mermaid Plugin for IntelliJ — Here's Why and What I Learned

If you use Mermaid diagrams in JetBrains IDEs, you've probably noticed the official plugin hasn't kept up. Architecture diagrams, kanban boards, treemaps — a good chunk of the newer diagram types either render poorly or not at all.

I ran into this problem in my own workflow. I use Mermaid a lot for technical documentation, and I had some of my diagrams broken in the Markdown preview. So instead of waiting for a fix that wasn't coming, I built my own plugin.

Six weeks later, Mermaid Visualizer is on the JetBrains Marketplace at v1.4.0, open-source under Apache 2.0.

The plugin listing on the JetBrains Marketplace


Why build yet another plugin?

I was looking for a concrete project to learn the IntelliJ plugin ecosystem from scratch. I had never built a plugin before, never touched JCEF, never published anything on the JetBrains Marketplace. Rather than building a throwaway exercise, I picked a real problem I was facing every day.

The existing landscape looked like this:

  • The official JetBrains Mermaid plugin — closed-source, not fully functional with the new diagrams
  • Mermaid Studio — active and feature-rich, but commercial and proprietary

There was a clear gap for an open-source, permissively licensed plugin running the latest version of Mermaid.js. That's the niche I went for.


What does it do?

Mermaid Visualizer embeds Mermaid.js v11.13.0 directly inside the IDE. No CDN, no external dependencies — everything works offline.

Markdown preview integration

Write a mermaid block in any Markdown file and it renders as a diagram in the built-in preview. All 24+ diagram types are supported: flowcharts, sequence diagrams, class diagrams, ER diagrams, Gantt charts, architecture diagrams, kanban, mindmaps, and everything else Mermaid supports.

A Markdown file with a mermaid block on the left, rendered diagram in the preview on the right

Dedicated editor for .mmd / .mermaid files

A split editor with live preview. You type on the left, the diagram updates on the right. Scroll sync keeps both sides aligned.

The split editor with a flowchart or sequence diagram

Export

Hover over any diagram (in the Markdown preview or the standalone editor) and an overlay toolbar appears. Copy as SVG, copy as PNG, or save to file.

The overlay toolbar visible on hover over a diagram

Zoom and pan

Ctrl + Scroll to zoom, click and drag to pan, fit-to-window, 1:1 view. Works the same in both contexts.

Syntax highlighting

Keywords, diagram types, arrows, strings, comments, directives — all highlighted with configurable colors via the standard IntelliJ color scheme settings.

A .mmd file showing syntax highlighting

Settings

Under Settings > Tools > Mermaid: theme (auto/default/dark/forest/neutral), look (classic or hand-drawn), font family, max text size, debounce delay. Changes apply live without reloading.

The Settings

Dark mode

Detected automatically. Switch your IDE theme and the diagrams follow.

Diagram in dark mode

Diagram in light mode


How it works under the hood

The core idea is simple: let Mermaid.js do the rendering, inside the IDE's embedded Chromium browser (JCEF).

The plugin bundles mermaid.min.js (about 2.9 MB) and loads it into a JCEF browser instance via loadHTML(). When you edit your diagram, the Kotlin side encodes the Mermaid source in base64, sends it to the browser through executeJavaScript(), and Mermaid.js renders it to SVG. Communication back to Kotlin (for export, errors, scroll sync) goes through JBCefJSQuery callbacks.

Execution diagram

Each rendered diagram lives inside a Shadow DOM to isolate its styles from the rest of the page. This matters especially in the Markdown preview where Mermaid's CSS could otherwise leak into your document.

For the Markdown integration specifically, the plugin hooks into the Markdown plugin's MarkdownBrowserPreviewExtension to inject its scripts and styles into the existing preview browser. It doesn't create a separate browser — it enhances the one that's already there.

The settings propagation has two different paths depending on the context. In the standalone editor, config changes trigger a re-render via an application-level message bus topic. In the Markdown preview, the new config is pushed through the Markdown plugin's BrowserPipe and JavaScript picks it up without a page reload.


What I learned

This was my first plugin, my first time publishing on the JetBrains Marketplace, and my first real encounter with several technologies. Here's what stood out.

JCEF is powerful but has sharp edges

The browser must be created on the EDT. loadHTML() is async, so you can't call executeJavaScript() right after — you need CefLoadHandler.onLoadEnd() to know when the page is ready. Resources loaded via file:// hit CORS restrictions, so inlining everything into loadHTML() turned out to be the most reliable approach. And onLoadEnd runs on CEF's IO thread, not the EDT, so you need to dispatch back for any shared state access.

The Markdown plugin's API requires some caution

The plugin integrates with the Markdown preview through browserPreviewExtensionProvider — the official extension point exposed by the Markdown plugin. This lets me inject scripts and styles into the existing preview browser without creating a separate one.

The approach is clean: the Markdown plugin renders `mermaid blocks as plain <code class="language-mermaid"> elements, and my JavaScript picks them up, feeds them to Mermaid.js, and replaces them with rendered SVG diagrams. No need to override the rendering pipeline itself.

I initially tried CodeFenceGeneratingProvider which seemed like the more direct approach — intercept the code block and generate HTML server-side. But its generateHtml() method is @ApiStatus.Internal and gets flagged by the plugin verifier. The JavaScript-based approach turned out to be both simpler and more robust: it works with IncrementalDOM (live updates as you type), handles theme changes dynamically, and doesn't depend on any internal API.

Shadow DOM saved me a lot of headaches

Mermaid.js generates SVGs with embedded <style> tags. Without isolation, these styles bleed into the Markdown preview and break the layout. Wrapping each diagram in a Shadow DOM solved this cleanly.

Publishing on the Marketplace is straightforward

The IntelliJ Platform Gradle Plugin handles most of the heavy lifting. Set up a PUBLISH_TOKEN, run ./gradlew publishPlugin, and you're live. The review process was quick. I automated releases with GitHub Actions: push a version tag, and CI builds, publishes, and creates a GitHub Release.

Keeping Mermaid.js up to date needs a strategy

The library is nearly 3 MB and gets frequent updates with new diagram types. I wrote a Gradle task (updateMermaid) that pulls the latest version from npm, and a GitHub Actions workflow that checks for updates and opens an issue when a new version is available.


What's next

The plugin covers the essentials — rendering, editing, export, zoom, settings. But there's more I want to build:

  • Code intelligence — Autocompletion for node names, diagram keywords, arrow types. Inspections for undefined nodes, unused nodes, invalid syntax. This means writing a proper Grammar-Kit parser for the main diagram types.
  • Navigation — Go-to-definition and Find Usages on node names. A Structure View showing the diagram hierarchy.
  • Live templates — Snippets for common diagram skeletons (mflow for flowchart, mseq for sequence, etc.).
  • ZenUML support — It requires a separate module that isn't included in the standard Mermaid bundle.

I don't know how far I'll get, but the roadmap is there and I'm working through it.


Try it out

If Mermaid is part of your workflow in a JetBrains IDE, give it a try:

It works in any JetBrains IDE based on build 2025.3+ (IntelliJ IDEA, WebStorm, PyCharm, GoLand, etc.). Just install it and your mermaid blocks will start rendering.

It's open-source, it's free, and I'm actively working on it. If something is missing, broken, or could be better — open an issue or drop a comment. Your feedback helps me improve the plugin and learn the platform.


If you want to support the project, you can buy me a coffee ☕.

Top comments (0)