I’d like to start a discussion around SVG rendering performance optimization.
I have a few reasons for sticking with SVG rendering, and I also have some ideas for improvement that I’d like to run by the group.
Why not go with WebGL?
On machines without a GPU, WebGL actually performs worse than SVG. In my work environment, GPU-less computers are pretty common, so this isn’t a corner case—it’s a real constraint.
Here’s the issue I’m hitting.
I published a grid-based table component made up of 168 elements
. When you drag it across the canvas, performance takes a serious hit. Here’s what that looks like in practice:
-
Dragging it on an empty canvas with no parent container—everything seems perfectly smooth.
-
Dropping that grid into an empty Frame and dragging it—it takes about 6 seconds for the table to redraw and become responsive after each drag.(The screenshots below were taken on my Mac M3. This is not the hardware-constrained environment I mentioned earlier.)
-
Adding a rectangle at the same hierarchy level as the grid—the page takes about 3 seconds to recover and become interactive again.
-
Dragging the grid into a sibling Frame—on top of the 6 seconds from scenario two, it adds another 4 seconds.
What I’m thinking so far
As far as I know, Penpot redraws affected SVG elements frame by frame in real time during drag interactions. For bulk drags like moving my grid table, that’s bound to be a huge performance bottleneck.
But here’s what I can’t quite figure out—I built an identical-looking table using Flex, and the drag performance is noticeably better. I honestly don’t know why.
I’ve already tried a few optimizations for grid layout updates under SVG rendering:
For grid trees with more than 50 nodes, I stopped redrawing the changed SVG portions on every frame during translation. Instead, I render a temporary thumbnail during the drag, and only redraw the full SVG once the translation is done.
For resizing the grid’s width and height, I replaced real-time redraws with just updating the highlighted selrect area during the drag, and defer the full grid redraw until after the drag ends.
While dragging a grid tree with over 50 nodes across other containers like Frames, I turned off drop target detection during the drag and delayed it until mouse-up.


