Why I Decided to Build a Lightweight Diagram Editor
Over the past few months, I’ve been building a lightweight diagram editor called CodePic.
It’s designed for flowcharts, ER diagrams, wireframes, sequence diagrams, and system architecture diagrams. At first I thought I was mostly building a “drawing tool,” but the deeper I got, the more I realized this is really about building a small editor engine.
Why not just use an existing solution?
When you start building something like this, the obvious first step is to look at what already exists:
- Mermaid
- SVG / DOM-based approaches
- whiteboard-style libraries
- existing flowchart toolkits
All of them solve part of the problem. But I still ended up building a lighter editor core myself.
The reason is pretty simple.
1. Mermaid is great for describing diagrams, not editing them
Mermaid is great when you want to express structure quickly. But once you move into the “I want to drag this, tweak that, and adjust the layout” stage, it becomes much less natural.
What I care about more is this:
After the diagram is generated, can it still be edited naturally?
2. Diagrams are not one-shot outputs
In real work, diagrams rarely end at the moment they are created.
After that, you usually still need to:
- adjust the layout
- rewrite labels
- add missing nodes
- reroute connectors
- regroup parts of the diagram
- adapt it to the real system or workflow
So the important question is not just whether a diagram can be generated.
It’s whether it stays easy to edit afterward.
3. I wanted a more controllable data model and interaction system
As soon as you start supporting things like:
- dragging
- multi-select
- zooming
- rotation
- nested containers
- connector syncing
- undo / redo
- text editing
you are no longer building a simple UI widget.
At that point, I wanted the editing behavior, data structure, and rendering flow to be fully under control instead of being scattered across components and ad hoc logic.
The structure I ended up with
At a high level, I split the editor into a few layers:
- interaction layer: pointer events, selection, dragging, tool switching
- action layer: all mutations go through a unified action entry
- state layer: document data and UI state
- rendering layer: paint elements to canvas from the current state
The most important rule here is:
UI components should not modify diagram data directly.
Once different entry points start mutating state on their own, problems show up quickly:
- one path forgets to write history
- one path forgets to trigger recomputation
- one path updates data but leaves the UI inconsistent
So I prefer routing all changes through the action layer. It makes undo/redo, constraints, templates, and AI-assisted workflows much easier to keep stable.
Why I chose Canvas
I ended up using Canvas as the main rendering layer instead of SVG / DOM.
Not because Canvas is automatically better, but because it fits the combination of features I wanted:
- more direct rendering when there are many elements
- better control over the hand-drawn style
- no need to turn every shape into a real DOM node
- rendering and hit-testing can be treated as separate problems
Of course, the tradeoffs are real:
- text editing can’t rely fully on native DOM behavior
- hit testing, transforms, and bounds need to be handled manually
- many things the browser would normally help with have to be rebuilt
But if the goal is a controllable editor, I think that tradeoff is worth it.
Text editing and connectors are harder than they look
When people think about diagram editors, they often focus on nodes first.
In practice, the harder parts are usually:
- text editing
- connector syncing
- nested containers
Text editing is a good example. Rendering text on Canvas is straightforward, but actual editing usually works better with an HTML overlay. That’s the more realistic way to handle cursor behavior, IME input, line wrapping, and general editing UX.
Connectors are another hidden source of complexity. They aren’t static shapes. They depend on other elements, and once you support anchors, auto-routing decisions, line types, and labels, that complexity grows quickly.
What I’ve become more convinced of
At first I thought I was building a drawing tool.
Now I think the hard part is something else:
How do you keep diagrams editable, iterative, and extensible without the editor becoming unstable?
That matters more to me than any single feature.
Because whether a diagram starts from a template, from manual drawing, or from AI generation, it always comes back to the same question:
Can the user keep editing it smoothly afterward?
Closing thought
If you’ve built something similar, I’d be curious how you approached it.
Especially these tradeoffs:
- Would you lean toward Canvas or SVG / DOM?
- Would you build text editing yourself, or use an overlay?
- Would you keep connector syncing highly automatic, or reduce automation?
If you want to check out what I’m building, here it is:
Top comments (0)