Snabbdom: A Modular Virtual DOM Library with Hooks and Custom Modules
A virtual DOM library with focus on simplicity, modularity, powerful features and performance.
At a glance
- What is it?
- Snabbdom is a TypeScript virtual DOM library with a core of around 200 lines that is designed to be composed from modules. Vue.js used Snabbdom as the basis for its virtual DOM implementation before Vue 3. Its patch function accepts any modules you choose and ignores the rest, keeping the bundle small by design.
- Who is it for?
- Snabbdom is best suited to developers who want a virtual DOM layer they can understand end to end, extend with custom modules, or embed in a framework. Its 200-SLOC core is readable and auditable.
- Can I use it commercially?
- Yes. MIT is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 106 days ago.
- What is it written in?
- Mainly TypeScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 30, 2026, and from our analysis. They are not legal advice.
Editorial analysis
What Snabbdom Solves and Its Design Philosophy
Snabbdom is a virtual DOM library built around the principle that the core should be minimal, and all non-essential functionality should be delegated to modules. The README states the core is approximately 200 lines of code and that it is designed to be extendable through custom modules.
The project started as a response to existing virtual DOM solutions the author found "too bloated, too slow, lacked features, had an API biased towards OOP." The patch function has a signature equivalent to a reduce/scan function, which is designed to integrate more naturally with functional reactive programming libraries.
Vue.js historically used Snabbdom as the foundation for its virtual DOM, which speaks to Snabbdom's design quality. The library is aimed at developers building their own framework on top of a virtual DOM, or at developers who want a well-understood, auditable virtual DOM without pulling in a full framework.
SVG support is built into the h helper. The README notes that SVG just works with h, meaning the helper correctly sets namespaces for SVG elements without extra configuration. The examples directory includes a carousel-svg and an svg example to demonstrate this. The library ships three live examples: animated element reordering, hero transitions between states, and an SVG carousel.
The Module System: How Snabbdom Stays Small
All functionality beyond diffing and patching is implemented as modules. The library ships with six built-in modules: classModule for toggling CSS classes, propsModule for setting DOM properties, attributesModule for HTML attributes, datasetModule for data-* attributes, styleModule for CSS styles with animation support, and eventListenersModule for attaching event listeners.
The README gives this example of initialising the patch function with a chosen set of modules:
import {
init,
classModule,
propsModule,
styleModule,
eventListenersModule,
h
} from "snabbdom";
const patch = init([
classModule,
propsModule,
styleModule,
eventListenersModule
]);The `init` function takes a list of modules and returns a `patch` function. Only the modules you pass are active. This pattern keeps the runtime footprint proportional to the features used.
Custom modules can hook into any stage of the diff and patch process using the hooks API. A module exports a set of hook functions, and init registers them at the appropriate points in the patch lifecycle. The module system is the mechanism by which developers extend Snabbdom for their specific needs without touching the core patch algorithm.
The patch Function, Hooks, and Thunks
The `patch` function returned by `init` takes two arguments: a DOM element or an old vnode, and a new vnode. It updates the DOM to match the new vnode description.
patch(oldVnode, newVnode);Snabbdom exposes lifecycle hooks at two levels. Per-vnode hooks fire when a specific node is created, inserted, updated, or removed. Global hooks run for all vnodes and are used when writing custom modules. The hooks available include: init (before a vnode's DOM node is created), insert (after it is added to the DOM), update, remove, and destroy.
Thunks are a performance optimisation for cases where subtrees do not need to be recomputed. A thunk wraps a function and its arguments; if the arguments have not changed, Snabbdom skips recomputing that part of the tree entirely. The README notes this as a way to "optimize the diff and patch process even further."
JSX support is included. TypeScript types for JSX are provided, and the README documents both TypeScript and Babel configurations for JSX compilation.
The style module supports three additional properties for CSS animations. Delayed properties apply styles only after the next animation frame, useful for entry animations. The remove hook on the style module applies styles when a node is being removed, allowing exit animations to run before the DOM node is deleted. Destroy properties apply when a node is being destroyed without animation. This set of hooks makes CSS-based entry and exit animations straightforward to implement in Snabbdom without a separate animation library.
Installing Snabbdom and Building a First Virtual Node
Install from npm:
npm install snabbdomThe package is distributed as ES modules. The `h` function creates virtual DOM nodes. A basic counter:
import { init, classModule, propsModule, styleModule, eventListenersModule, h } from "snabbdom";
const patch = init([classModule, styleModule, eventListenersModule]);
const container = document.getElementById("container");The `h` function accepts a selector string (`div#id.class`), an optional data object, and children. The package.json specifies `"sideEffects": false`, which means bundlers can tree-shake unused code. The library is a pure ES module with main entry at `build/index.js` and TypeScript types at `build/index.d.ts`. Node.js 12.17.0 or newer is required.
The toVNode function converts an existing DOM element into a vnode, which is useful for hydrating server-side rendered HTML. The virtual node data structure has six fields: sel (the CSS selector), data (the module data object), children (an array of child vnodes), text (text content), elm (the real DOM element, set after patching), and key (a unique identifier for efficient list diffing). The key field is important for lists: without keys, Snabbdom must rely on position when reconciling list updates.
Where Snabbdom Falls Short
Snabbdom is not a complete application framework. It provides virtual DOM diffing and patching but no state management, routing, server-side rendering, or component model. Developers who need these must provide or build them. The README links to third-party packages for server-side HTML output (snabbdom-to-html), compact vnode creation (snabbdom-helpers), template string support (snabby), and vnode assertion for testing (snabbdom-looks-like).
Unmounting a component is not directly supported via a dedicated API. The README describes a workaround: patching to a comment vnode simulates unmounting. Applications that need clean component unmounting semantics will find this indirect approach more complex than frameworks with built-in lifecycle management.
The fragment API, which allows returning multiple top-level nodes without a wrapper element, is documented as experimental in the README. Production use should account for possible changes to this API.
The package.json shows Snabbdom uses a web-test-runner for unit tests rather than Jest or Vitest. Tests are run in a real browser environment via Browserstack for cross-browser compatibility testing. This testing approach is documented in the README as a community acknowledgement. The eslint.config.mjs and .husky/ pre-commit hooks enforce code quality on contributions.
Snabbdom Versus React: Different Goals, Different Trade-offs
React is the dominant virtual DOM library and the most direct comparison. React includes a component model, a state management API (hooks), server-side rendering, a developer tooling ecosystem, and a large package ecosystem. Snabbdom includes none of these. Snabbdom's core is 200 SLOC; React's codebase is orders of magnitude larger.
The trade-off is explicit control versus bundled convenience. A developer using Snabbdom knows exactly what the virtual DOM layer does and can read the entire core in an afternoon. A developer using React works within a larger system with more abstractions. For building a lightweight custom UI runtime, embedding a virtual DOM in a specialised tool, or understanding how virtual DOM diffing works from first principles, Snabbdom is more approachable than React. The source is in src/ and totals a handful of files, each addressing one concern: the core patch algorithm, the h function, and the module interface.
The npm package version is 3.6.4, released on 2026-06-17. The repository's last push was on the same date. v3.6.3 shipped on 2025-09-30. The project is MIT licensed.
Editorial conclusion
Snabbdom is best suited to developers who want a virtual DOM layer they can understand end to end, extend with custom modules, or embed in a framework. Its 200-SLOC core is readable and auditable. The last push to the repository was on 2026-06-17, with v3.6.4 released on that date. Teams building large applications with rich tooling expectations are better served by React or Vue, which have larger ecosystems. Snabbdom is most valuable when the full scope of those frameworks would be overhead, and when a clean, extensible virtual DOM with predictable performance is the actual requirement.
Frequently asked questions
What is the difference between virtual DOM and shadow DOM?
Virtual DOM is a JavaScript technique where a lightweight representation of the DOM is maintained in memory and reconciled with the real DOM to minimise updates. Shadow DOM is a browser-native API for encapsulating component markup and styles from the rest of the page. The two serve different purposes and operate independently.
Is React still using virtual DOM?
React has historically used a virtual DOM, but the README does not document React's internal implementation. Snabbdom is its own virtual DOM implementation and is not affiliated with React.
How does Snabbdom handle event listeners?
Snabbdom includes an eventListenersModule that attaches event listeners when specified in the vnode's data object. Listeners are cleaned up automatically when the vnode is removed from the DOM, which prevents memory leaks from dangling handlers.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/snabbdom-snabbdom)