Compile Ready
Module 14 · Machine Coding

Build: Mini Virtual DOM

Advanced16m read55m practice1h 11m total
Machine CodingVirtual DOMDiffingRendering

Introduction

A mini virtual DOM challenge asks you to model UI as plain JavaScript objects, render that tree, and compute differences between two trees. The interview goal is not to rebuild React; it is to demonstrate tree representation, recursion, escaping, props comparison, and patch generation.

This version stays worker-safe by rendering to strings and producing plain-object patches instead of touching the real DOM.

Why This Matters

Virtual DOM questions test whether you understand the ideas behind modern UI libraries: declarative trees, pure render output, reconciliation, keys, and patching. A compact implementation gives you vocabulary for discussing React without hand-waving.

Theory

Virtual node shape

A virtual node can be represented as { type, props, children }. Text nodes are just strings or numbers. The helper h(type, props, ...children) flattens children and removes empty values.

Rendering

Rendering to a string is a pure version of DOM rendering. Escape text and attribute values to prevent HTML injection. Boolean attributes can be rendered by name when true and omitted when false.

Diffing

A basic diff compares node pairs at the same path. If one side is missing, create or remove. If primitive text differs, emit a text patch. If element type differs, replace. If type matches, diff props and recurse into children.

Follow-up: keys

This simple diff compares children by index. Real libraries use keys to detect moves and preserve component state across reorderings. Mentioning keyed diffing is an important follow-up in interviews.

Visual Diagrams

Virtual DOM pipeline
h calls
  |
  v
virtual tree
  |
  +--> renderToString -> HTML string
  |
  +--> diff old tree vs new tree -> patch list

The tree is plain data, so rendering and diffing can be pure functions.

Code Examples

Complete mini virtual DOM

The implementation is intentionally pure: no document, no real DOM, and no framework dependencies.

Loading…

Playground

Loading editor…
Console

Press Run to execute the code and see output here.

Output Prediction

Predict the output #1

javascript
1function h(type, props) {
2 var children = [];
3 for (var index = 2; index < arguments.length; index += 1) {
4 children.push(arguments[index]);
5 }
6 return { type: type, props: props || {}, children: children };
7}
8
9function diff(oldNode, newNode, path) {
10 var currentPath = path || 'root';
11
12 if (!oldNode) {
13 return [{ type: 'CREATE', path: currentPath }];
14 }
15
16 if (!newNode) {
17 return [{ type: 'REMOVE', path: currentPath }];
18 }
19
20 var oldIsText = typeof oldNode === 'string' || typeof oldNode === 'number';
21 var newIsText = typeof newNode === 'string' || typeof newNode === 'number';
22
23 if (oldIsText || newIsText) {
24 return oldNode === newNode ? [] : [{ type: 'TEXT', path: currentPath }];
25 }
26
27 if (oldNode.type !== newNode.type) {
28 return [{ type: 'REPLACE', path: currentPath }];
29 }
30
31 var patches = [];
32 var oldClass = oldNode.props.class;
33 var newClass = newNode.props.class;
34
35 if (oldClass !== newClass) {
36 patches.push({ type: 'PROPS', path: currentPath });
37 }
38
39 var max = Math.max(oldNode.children.length, newNode.children.length);
40 for (var index = 0; index < max; index += 1) {
41 patches = patches.concat(diff(oldNode.children[index], newNode.children[index], currentPath + '.' + index));
42 }
43
44 return patches;
45}
46
47var oldTree = h('ul', {}, h('li', { class: 'item' }, 'A'));
48var newTree = h('ul', {}, h('li', { class: 'active' }, 'A'), h('li', {}, 'B'));
49console.log(diff(oldTree, newTree).map(function (patch) {
50 return patch.type + ':' + patch.path;
51}).join('|'));

Coding Exercises

Implement a mini virtual DOM

Hard

Build a pure mini virtual DOM.

Requirements:

  • h(type, props, ...children) returns a virtual node and flattens nested child arrays.
  • Ignore null, undefined, and false children.
  • renderToString(node) returns escaped HTML.
  • Boolean true props render as attributes without values.
  • False, null, and undefined props are omitted.
  • diff(oldNode, newNode) returns patches with paths.
  • Patches should cover create, remove, replace, text, and props changes.

Constraints:

  • Do not use the real DOM.
  • Do not use a framework.
  • Keep the implementation pure and runnable in a worker.

Interview Questions

1Why do real virtual DOM libraries use keys?

Keys let the diff algorithm match logical children across reorders, insertions, and deletions. Without keys, index-based diffing may replace or mutate the wrong child and can lose component state.

Asked at:MetaGoogle

Follow-ups

  • How would you implement keyed child diffing?
  • How does React Fiber change scheduling?
2Why escape text and attribute values in `renderToString`?

Rendering user-controlled strings without escaping can create HTML injection or XSS vulnerabilities. Escaping turns special characters into safe entities.

Quiz

1. What patch should be emitted when two virtual nodes have different `type` values at the same path?

Summary

  • A virtual DOM tree is plain data: type, props, and children.
  • `renderToString` should escape text and attribute values.
  • A basic diff handles create, remove, replace, text, and prop patches.
  • Index-based child diffing is simple; keyed diffing is the production follow-up.

Cheat Sheet

Mini VDOM checklist

  • h(type, props, ...children) builds plain nodes.
  • Flatten child arrays; ignore null, undefined, false.
  • Text nodes can be strings or numbers.
  • Escape HTML in text and attributes.
  • Render boolean true attributes by name.
  • Diff paths like root.0.1.
  • Cases: create, remove, text, replace, props.
  • Mention keys for reorder-aware reconciliation.