The Weight of the VDOM
For three years, our studio treated React as the default air we breathed. We built complex generative art interfaces, high-frequency data visualizations, and interactive canvases, all wrapped in a thick layer of synthetic event listeners and reconciliation logic. Then, we hit the wall. During a particularly demanding project involving thousands of concurrent SVG nodes controlled by WebGL textures, the overhead of the Virtual DOM became a parasitic entity. Every interaction stuttered. Every frame felt heavy. We were fighting the framework to talk to the GPU.
So, we stopped. We gutted the architecture. We moved to pure Web Components.
The Illusion of Developer Experience
The industry loves the term 'developer experience' (DX), but it often acts as a euphemism for hiding complexity behind magic. When you use React, you are not writing for the browser. You are writing for a state-management machine that periodically decides if your UI is 'out of sync.' For creative artifacts—the kind of work where a mouse-move event needs to trigger a shader uniform update in under 16ms—that abstraction is a liability.
We found that CustomElements and the Shadow DOM gave us the encapsulation we were pretending to get from styled-components or CSS-in-JS, but without the dependency bloat. No more node_modules folder the size of a small moon. No more fighting with useMemo hooks just to prevent a canvas re-render. We just use native JavaScript classes.
class CreativeCanvas extends HTMLElement {
constructor() {
super();
this.attachShadow({ mode: 'open' });
this.canvas = document.createElement('canvas');
this.shadowRoot.appendChild(this.canvas);
}
connectedCallback() {
this.initWebGL();
}
}Shadow DOM: The Real Scoped CSS
The biggest lie in the modern JS ecosystem is that you need a build step to keep your CSS clean. We spent weeks chasing bugs in React projects where global styles leaked into scoped components. With the Shadow DOM, the browser enforces the boundary. It is ironclad. We write standard CSS files, we import them, and they stay exactly where we put them. The performance win is massive; we no longer rely on a runtime CSS-in-JS engine to inject thousands of style tags into the document head.
The Cost of Independence
Switching was not a frictionless joyride. We lost the convenience of the massive component library ecosystem. We had to rethink how we handle state. Instead of prop drilling, we leaned into custom events and the BroadcastChannel API. It forces a more decoupled architecture, which, frankly, was the best thing that ever happened to our codebase.
The most efficient code is the code that isn't there. If your framework requires a complex build chain just to render a button, you are paying a debt that your users will eventually collect.
Performance as a Feature
When we profiled our latest installation, the memory usage dropped by 60%. The garbage collector, previously spinning its wheels trying to clean up React's synthetic event garbage, went silent. We are now pushing 60fps on devices that previously crashed under the weight of a standard 'modern' web application. The lesson is simple: browsers have matured. They are no longer just rendering engines for static documents; they are robust operating systems. We don't need to rebuild the OS on top of the OS.
The Provocation
If your studio is chasing the latest framework trend, stop. Ask yourself if your complexity is serving the user or simply serving your comfort. We replaced a sprawling React application with a set of lightweight, native Web Components that weigh nothing and run everywhere. The code is predictable. The performance is rock solid. And most importantly, it will still work in five years, long after the current flavor-of-the-month framework has been relegated to the legacy heap.
Stop building for the framework. Start building for the browser.
READER ESSAY DISCUSSIONS (0)
Share your architectural perspectives, technical notes, or critique.
No discussions recorded yet.
Be the first to spark the dialogue on this essay.