The Story of DOM Patch

speckx1 pts0 comments

The Story of DOM Patch · Caolan

The Story of DOM Patch

Up: Programming notes

Thursday, 16 April 2026

Today, I'm sharing a library that efficiently patches the DOM to match a desired tree. It's small (900 bytes), fast (see benchmarks), and the code is short and understandable.

Source code

Benchmarks

But you'll likely find it unremarkable as a library. Who needs another React, eh? So instead of explaining its API (which you can find in the README), I thought I'd share the journey that led me to it.

The Single Page Application

I worked a lot with CouchApps early in my career. These were an unsanctioned hack which allowed you to host static assets on top of CouchDB. Because CouchDB has a REST API, you could use AJAX requests to produce a reasonably complete application in very little time.

This was also around the time that Single Page Applications (SPAs) were gaining traction. And I, being a JavaScript fan holding a REST API, built a lot of applications in this style.

But SPAs were a particular headache for public-facing websites because search engine crawlers at the time did not execute JavaScript.

SEO and Isomorphic JavaScript

One solution I experimented with was to execute some of my JavaScript server-side. In this case, on the CouchDBs view engine (eek).

I created an API that would render pages client-side but also run on the server. It's a technique that became known as 'Isomorphic JavaScript'.

I don't claim any originality there. Thanks to Node.js, it has been attempted many times before and since. Perhaps most famously today by Next.js. But I was very quickly convinced Isomorphic JavaScript is a dead end. The subtle differences between environment (server vs. client) mean consumers of your library must be very careful to use it correctly. It demands complex tooling to help them target two environments at once and, as we know, everyone loves a complex build chain.

So instead of chasing the one true API for all environments, I settled on sharing just the templates. I'd write a template in Handlebars and share it between client and server, duplicating the code around the template as necessary.

DOM stability

The problem with Handlebars or Mustache templates is that they completely wipe and replace the DOM inside the element you're updating. Even if that DOM contained a form element the user was busy typing in.

But, thanks to React, a possible solution was on the horizon: DOM diffing and patching. At the time, React had no good answer to server rendered content but I thought I could use its patching algorithm to write a new template engine. One that allowed me to share templates between client and server without clobbering the DOM. I called this experiment Magery.

{{#define main}}<br>{{title}}

{{#each items}}<br>{{name}}<br>{{/each}}

Add<br>{{/define}}

Blog post about Magery (2016)

Magery had a clever JavaScript implementation that would patch the DOM and a dumb server-side implementation that would render simple strings. My hope was that, by keeping the server implementation simple, I could hold the door open to non-JavaScript languages on the server-side.

I built a functional prototype but I felt it demanded too much early optimisation from adopters. It asked them to choose a non-standard templating library on the server just in case they needed DOM patching at a later date.

Maybe it was HTML all along

So single page applications have problems, shared templates have problems, and even fancy shared templates with DOM patching have problems. Maybe I was asking the wrong question?

Because all of these techniques can work well. In specific circumstances. The problem is trying to define a single solution that works everywhere.

Just as I can't tell you which grain of sand makes a heap, I can't name the line of code that will make your simple CGI script into a 'Web Application' demanding a 'Framework'. So, like many seasoned programmers before me, I have come to prefer simple solutions easily grown.

In practice, that often means starting with simple server-rendered HTML using the default tools available to you. Later, it might mean adding JavaScript enhancements via Custom Elements.

JavaScript Features to Build Slowly Upon: Custom Elements

And later, should the network of dependencies between DOM and rendered data grow entangled, you might even choose to introduce DOM patching.

Ideally, each step of this process should be smooth and avoid a rewrite. That's why I wrote the DOM Patch library. I can use it to patch the internals of a Custom Element when useful without having to adopt a large framework elsewhere.

Example

Here's a (contrived) Custom Element that updates using DOM Patch.

import { Patcher } from "./patch.js";

class MyCounter extends HTMLElement {<br>counter = 0;<br>connectedCallback() {<br>this.interval = setInterval(() => {<br>this.counter++;<br>this.render();<br>}, 1000);<br>this.render();<br>disconnectedCallback() {<br>clearInterval(this.interval);<br>render() {<br>new Patcher(this).patch(root =>...

server javascript patch library render templates

Related Articles