I Built a Virtual DOM Before React Existed (And Nobody Cared)
In 2012, I created a JSON-based virtual DOM system. A year later, React launched with the exact same concept. Here's why being first doesn't matter.
2012. I have a problem. I am about to found a VC-backed company to create a nocode tool.
I need to declaratively describe user interfaces that can be:
Generated on the server
Edited in a visual tool
Rendered in the browser
Updated efficiently
My solution: A JSON-based structure representing the DOM as data.
2013. React launches.
I watch the announcement video. My jaw drops.
"Wait... that's literally what I built."
📖 The Origin Story
The Problem (2013)
I was building Bondlayer, a visual tool for creating websites without code.
The workflow needed to be:
Designer → Visual Editor → JSON structure → Generated websiteBut how do you represent a website as data?
The naive approach:
// Generate HTML strings (yikes)
const html =
<div class="hero">
<h1>${title}</h1>
<p>${content}</p>
</div>
;Problems:
Hard to manipulate (string concatenation hell)
Can't efficiently update parts
No way to diff changes
XSS vulnerabilities everywhere
I needed something better.
The Shadow DOM Whisper
2012. I'm researching JavaScript frameworks.
I stumble across Shadow DOM, a browser spec for encapsulated DOM trees.
// Shadow DOM (simplified)
const shadowRoot = element.attachShadow({mode: 'open'});
shadowRoot.innerHTML = '<p>Hidden content</p>';The insight:
"If browsers represent DOM as a tree internally, why can't I represent it as JSON externally?"
The JSON Virtual DOM (Before It Was Cool)
My idea:
Represent the entire UI as a JSON structure:
{
"type": "div",
"props": {
"className": "hero"
},
"children": [
{
"type": "h1",
"props": {},
"children": ["{{title}}"]
},
{
"type": "p",
"props": {},
"children": ["{{content}}"]
}
]
}Benefits:
Serializable (save to database)
Declarative (describe what, not how)
Transformable (visual editor can manipulate it)
Renderable (turn into DOM or HTML)
I started building a renderer.
🛠️ Building the Renderer (Late 2012)
Pure Functions to Render JSON
// My "virtual DOM" renderer (before React)
const render = (vnode, data) => {
// Base case: Text node
if (typeof vnode === 'string') {
return document.createTextNode(vnode);
}
// Create element
const el = document.createElement(vnode.type);
// Set properties
Object.keys(vnode.props || {}).forEach(key => {
if (key === 'className') {
el.className = vnode.props[key];
} else {
el.setAttribute(key, vnode.props[key]);
}
});
// Render children recursively
(vnode.children || []).forEach(child => {
// Replace template variables
if (typeof child === 'string' && child.includes('{{')) {
const key = child.replace(/{{|}}/g, '');
child = data[key] || '';
}
el.appendChild(render(child, data));
});
return el;
};
// Usage const vdom = { type: "div", props: { className: "hero" }, children: [ { type: "h1", children: ["{{title}}"] } ] };
const dom = render(vdom, { title: "Hello World" }); document.body.appendChild(dom);
It worked!
But it was naive. Re-rendering meant destroying and rebuilding the entire tree.
The Diffing Problem
I needed efficient updates:
// User edits title in visual editor
// Old state
{
type: "div",
children: [
{ type: "h1", children: ["Old Title"] }
]
}
// New state { type: "div", children: [ { type: "h1", children: ["New Title"] } // Only this changed ] }
// Problem: How to update only what changed?
I started building a diff algorithm.
// Simple diff (very naive)
const diff = (oldVNode, newVNode) => {
// Different types? Replace entire node
if (oldVNode.type !== newVNode.type) {
return { type: 'REPLACE', node: newVNode };
}
// Same type, diff props
const propsDiff = diffProps(oldVNode.props, newVNode.props);
// Diff children recursively
const childrenDiff = diffChildren(
oldVNode.children,
newVNode.children
);
return {
type: 'UPDATE',
props: propsDiff,
children: childrenDiff
};
};This was getting complex. Fast.
I was building a framework.
🎪 The Framework Hunt (2013)
While building my JSON renderer, I tested other frameworks:
Backbone.js
// Imperative, manual DOM manipulation
const View = Backbone.View.extend({
render: function() {
this.$el.html(this.template(this.model.toJSON()));
return this;
}
});Problem: Still too imperative. I wanted declarative.
Angular 1.x
<!-- Declarative, but HTML templates -->
<div ng-controller="MyCtrl">
<h1>{{title}}</h1>
</div>Problem: Templates were HTML strings, not data structures. Can't serialize easily.
Ember.js
// Handlebars templates
App.ApplicationView = Ember.View.extend({
templateName: 'application'
});Problem: Same issue, templates as strings, not data.
None of these matched what I needed:
I needed:
✅ Declarative UI
✅ Serializable structure (JSON)
✅ Efficient updates (diffing)
✅ Server-side rendering
So I kept building my own.
💥 Then React Launched (2013)
May 2013. Facebook announces React at JSConf.
I watch the video:
// React code (2014)
const Component = React.createClass({
render() {
return (
<div className="hero">
<h1>{this.props.title}</h1>
</div>
);
}
});My reaction:
"Wait... that JSX compiles to... objects?!"
// What JSX becomes
React.createElement(
'div',
{ className: 'hero' },
React.createElement('h1', null, this.props.title)
);
// Which creates { type: 'div', props: { className: 'hero' }, children: [ { type: 'h1', props: {}, children: ['Hello'] } ] }
THAT'S EXACTLY MY JSON STRUCTURE.
They called it Virtual DOM.
I called it JSON DOM Layer (hence the Bondlayer).
Same concept.
The Virtual DOM Concept
React's pitch:
UI = f(state)
When state changes:
Generate new virtual DOM
Diff with old virtual DOM
Update only what changed in real DOM
Efficient, declarative updates!
My pitch (that I never publicly made):
UI = JSON structure
When user edits in visual tool:
Update JSON
Diff with previous JSON
Update only changed elements
Efficient, declarative updates!
Literally the same thing.
😮 The Realization
I had independently discovered the virtual DOM concept.
But React had:
✅ Facebook's backing
✅ JSX (syntactic sugar)
✅ A compelling pitch
✅ Marketing and evangelism
✅ Open source release
I had:
❌ A JSON structure nobody knew about
❌ No marketing
❌ Closed source (part of Bondlayer)
❌ No community
🔄 The Pivot
My decision: Don't compete. Adopt.
Within weeks, I migrated Bondlayer to React.
// Old: My custom JSON renderer
const dom = render(jsonStructure, data);
// New: React renderer
const Canvas = ({ structure, data }) => {
const renderVNode = (vnode) => {
const Component = componentMap[vnode.type];
return ( <Component {...vnode.props}> {vnode.children?.map(renderVNode)}
</Component> );
};
return renderVNode(structure);
};
The migration was shockingly smooth.
Why? Because my JSON structure already matched React's virtual DOM!
React was the perfect fit. Like they built it for us.
🤔 Why React Won (And I Didn't)
It Wasn't About Being First
I had a JSON virtual DOM in 2012. React launched in 2013.
But:
I didn't:
Write blog posts
Give talks
Open source it
Evangelize the concept
Build a community
React did all of that.
The Zeitgeist
Here's the thing: Good ideas emerge independently when the time is right.
Other people were thinking about virtual DOMs too:
Virtual DOM diffing wasn't a new concept (games had been doing similar for years)
The problem (inefficient DOM updates) was universal
The solution (declarative + diffing) was logical
React wasn't magic. It was an idea whose time had come.
React just executed and marketed better than everyone else.
What React Did Right
1. JSX (Syntax Sugar)
// My JSON (harder to write)
{
type: "div",
props: { className: "hero" },
children: [
{ type: "h1", children: ["Title"] }
]
}
// React JSX (easier to write) <div className="hero"> <h1>Title</h1> </div>JSX made virtual DOM feel like HTML. Genius.
2. Facebook's Credibility
"Used in production at Facebook" = instant trust
Nobody cared that I was using my JSON DOM in production. I wasn't Facebook.
3. Open Source
React was free. Open. Inspectable.
My implementation was closed source (part of Bondlayer).
4. Documentation & Evangelism
React had:
Clear documentation
Conference talks
Blog posts
Community advocacy
I had... a JSON structure in a codebase nobody saw.
5. Timing
2014 was perfect for React:
Single Page Apps were becoming mainstream
jQuery was showing its age
Developers wanted something better
The frontend ecosystem was ready for a paradigm shift
React rode the wave. I was too early (or too isolated).
💡 What I Learned
1. Innovation ≠ Success
Having a good idea first doesn't matter.
Executing and marketing the idea matters.
I had the idea. React executed it better.
2. Open Source is Marketing
If I had open-sourced my JSON DOM renderer in 2012:
Maybe someone would have used it
Maybe I'd have gotten feedback
Maybe it would have evolved faster
Maybe I'd have built a community
Or maybe not. But I'll never know.
3. Technical Innovation Needs Evangelism
Even brilliant ideas die in obscurity without:
Blog posts
Conference talks
Documentation
Developer advocacy
Code alone isn't enough.
4. The Best Tool Wins (Not The First Tool)
React wasn't first. But it was:
Best documented
Best marketed
Best supported
Best developer experience
That's why React won.
5. Collaboration > Isolation
I built my JSON DOM in isolation.
React was built:
At Facebook (collaboration with teams)
In open source (collaboration with community)
With feedback loops (talks, blog posts, issues)
Isolation breeds slow innovation.
Collaboration breeds fast iteration.
🎯 The Silver Lining
Perfect Architecture Match
When React launched, my architecture already supported it.
Migration took days, not months.
Other companies spent years migrating to React.
We just dropped in a new renderer.
Why? Because I'd been thinking in virtual DOM terms all along.
Deep Understanding
Building my own virtual DOM taught me:
How React works under the hood
Why reconciliation matters
When to optimize renders
How to debug performance
I understand React better because I built something like it.
Timing Validation
React's success validated my architectural thinking.
I wasn't crazy. The JSON structure made sense.
The market just needed Facebook to tell them that.
The Conclusion
In 2012, I built a JSON-based virtual DOM.
In 2013, React launched with the same concept and changed web development forever.
Did I feel robbed? For about 5 minutes.
Then I felt grateful:
React validated my architecture. Gave me a better tool. Saved me from maintaining a framework. Let me focus on building products instead.
The lesson:
Being first is overrated. Being effective is underrated.
React was effective. I was early.
I'll take effective over early any day.
P.S. To this day, Bondlayer still uses a JSON structure that renders to React. The architecture I designed in 2013 still works in 2026.
Something to add or contest? I'm always open to technical debate.
Start a discussion