Back to Notes
engineering

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 website

But 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