AboutBlogContact
Frontend EngineeringMarch 22, 2017 2 min read 123Updated: June 22, 2026

Preact Internals: A Minimal VDOM Implementation (2017)

AunimedaAunimeda
📋 Table of Contents

Preact Internals: A Minimal VDOM Implementation

The JavaScript world is obsessing over bundle sizes. While React is the industry standard, Preact has carved out a niche by being incredibly small without sacrificing the API. How does it manage to fit a full diffing algorithm and component lifecycle into 3kb?

The VNode Structure

At its core, a Virtual DOM node is just a plain object. No complex classes, no overhead.

function h(nodeName, attributes, ...args) {
    let children = args.length ? [].concat(...args) : null;
    return { nodeName, attributes, children };
}

The Diffing Algorithm

Preact's secret is that it diffs directly against the real DOM. Unlike React, which maintains a separate virtual tree, Preact uses the DOM itself as the source of truth for the "old" state during the diff.

function render(vnode, parent) {
    let oldDom = parent.firstChild;
    diff(oldDom, vnode, parent);
}

function diff(dom, vnode, parent) {
    if (typeof vnode === 'string') {
        if (dom && dom.nodeType === 3) {
            if (dom.nodeValue !== vnode) dom.nodeValue = vnode;
        } else {
            let newDom = document.createTextNode(vnode);
            if (dom) parent.replaceChild(newDom, dom);
            else parent.appendChild(newDom);
        }
        return;
    }

    let out = dom;
    if (!dom || dom.nodeName.toLowerCase() !== vnode.nodeName.toLowerCase()) {
        out = document.createElement(vnode.nodeName);
        if (dom) {
            while (dom.firstChild) out.appendChild(dom.firstChild);
            parent.replaceChild(out, dom);
        } else {
            parent.appendChild(out);
        }
    }

    // Diff attributes and children...
    diffAttributes(out, vnode.attributes);
    diffChildren(out, vnode.children);
}

Component Lifecycle

Components in Preact are simply functions or classes that return VNodes. The render loop checks if nodeName is a function, and if so, instantiates it or calls it.

function buildComponentFromVNode(dom, vnode) {
    let c = new vnode.nodeName(vnode.attributes);
    let rendered = c.render();
    return diff(dom, rendered);
}

By avoiding the synthetic event system and keeping the diffing logic surgical, Preact proves that you don't need a massive library to build complex interfaces. It's the ultimate "less is more" for the modern web.


Aunimeda builds modern web frontends - from single-page applications to complex multi-locale sites.

Contact us to discuss your frontend project. See also: Web Development, Corporate Website Development

Read Also

Next.js 13/14: Server Actions and the New App Router (2023)aunimeda
Frontend Engineering

Next.js 13/14: Server Actions and the New App Router (2023)

React is coming full circle. In 2023, Next.js Server Actions are bringing back the simplicity of PHP and Ruby on Rails with modern React components.

React Server Components: Decoding the Wire Format (2023)aunimeda
Frontend Engineering

React Server Components: Decoding the Wire Format (2023)

RSC is finally stable in Next.js 13.4. But what's actually happening in that .rsc stream? Let's decode the secret language of the server-client bridge.

Zero-Runtime CSS-in-JS: The Performance King (2023)aunimeda
Frontend Engineering

Zero-Runtime CSS-in-JS: The Performance King (2023)

Is Styled Components dead? In 2023, zero-runtime CSS-in-JS is taking over. No more runtime script, no more style re-calculation.

Need IT development for your business?

We build websites, mobile apps and AI solutions. Free consultation.

Web Development

Get Consultation All articles