Domvm
DOM ViewModel - A thin, fast, dependency-free vdom view layer
Install / Use
/learn @domvm/DomvmREADME
A thin, fast, dependency-free vdom view layer (MIT Licensed)
Introduction
domvm is a flexible, pure-js view layer for building high performance web applications. Like jQuery, it'll happily fit into any existing codebase without introducing new tooling or requiring major architectural changes.
- It's zero-dependency and requires no compilation or tooling; one
<script>tag is all that's needed. - It's small: ~6k gz, fast: just 20% slower vs painfully imperative vanilla DOM code. 2x faster SSR vs React v16.
- Its entire, practical API can be mastered in under 1 hour by both, OO graybeards and FRP hipsters. Obvious explicit behavior, debuggable plain JS templates, optional statefulness and interchangable imperative/declarative components.
- It's well-suited for building simple widgets and complex, fault-tolerant applications.
- Supports down to IE11 with a tiny Promise shim.
To use domvm you should be comfortable with JavaScript and the DOM; the following code should be fairly self-explanatory:
var el = domvm.defineElement,
cv = domvm.createView;
var HelloView = {
render: function(vm, data) {
return el("h1", {style: "color: red;"}, "Hello " + data.name);
}
};
var data = {name: "Leon"};
var vm = cv(HelloView, data).mount(document.body);
Demo Playground

Documentation
- What domvm Is Not
- Builds
- Changelog
- Tests
- Installation
- DEVMODE
- Templates
- Views
- Parents & Roots
- Sub-views vs Sub-templates
- Event Listeners
- Autoredraw
- Streams
- Refs & Data
- Keys & DOM Recycling
- Hello World++
- Emit System
- Lifecycle Hooks
- Third-party Integration
- Extending ViewModel & VNode
- createContext
- Isomorphism & SSR
- Optimizations
- WIP: https://github.com/domvm/domvm/issues/156
What domvm Is Not
As a view layer, domvm does not include some things you would find in a larger framework. This gives you the freedom to choose libs you already know or prefer for common tasks. domvm provides a small, common surface for integration of routers, streams and immutable libs. Some minimalist libs that work well:
- Routing: domvm-router, riot/route, rlite, navigo
- Ajax/fetch/XHR: xr, alite
- Streams: flyd, xstream
- Immutable stores: Freezer, MobX
- Vendor prefixing: cssprefix, prefixfree
- CSS-in-JS: linaria, stylis.js, j2c, emotion, oh boy...
Many /demos are examples of how to use these libs in your apps.
Builds
domvm comes in several builds of increasing size and features. The nano build is a good starting point and is sufficient for most cases.
Changelog
Changes between versions are documented in Releases.
Tests
- Tests run in a browser: https://domvm.github.io/domvm/test/
- Coverage reports are generated via
npm run covtest && npm run covreport - Current coverage is 85% - 90%
Installation
Browser
<script src="dist/nano/domvm.nano.iife.min.js"></script>
Node
var domvm = require("domvm"); // the "full" build
DEVMODE
If you're new to domvm, the dev build is recommended for development & learning to avoid common mistakes; watch the console for warnings and advice.
There are a couple config options:
domvm.DEVMODE.mutations = falsewill disable DOM mutation logging.domvm.DEVMODE.warnings = falsewill disable all warnings.domvm.DEVMODE.verbose = falsewill suppress the explanations, but still leave the error names & object info.domvm.DEVMODE.UNKEYED_INPUT = falsewill disable only these warnings. The full list can be found in devmode.js.
Due to the runtime nature of DEVMODE heuristics, some warnings may be false positives (where the observed behavior is intentional). If you feel an error message can be improved, open an issue!
While not DEVMODE-specific, you may find it useful to toggle always-sychronous redraw during testing and benchmarks:
domvm.cfg({
syncRedraw: true
});
Templates
Most of your domvm code will consist of templates for creating virtual-dom trees, which in turn are used to render and redraw the DOM. domvm exposes several factory functions to get this done. Commonly this is called hyperscript.
For convenience, we'll alias each factory function with a short variable:
var el = domvm.defineElement,
tx = domvm.defineText,
cm = domvm.defineComment,
sv = domvm.defineSvgElement,
vw = domvm.defineView,
iv = domvm.injectView,
ie = domvm.injectElement,
cv = domvm.createView;
Using defineText is not required since domvm will convert all numbers and strings into defineText vnodes automatically.
Below is a dense reference of most template semantics.
el("p", "Hello") // plain tags
el("textarea[rows=10]#foo.bar.baz", "Hello") // attr, id & class shorthands
el(".kitty", "Hello") // "div" can be omitted from tags
el("input", {type: "checkbox", checked: true}) // boolean attrs
el("input", {type: "checkbox", ".checked": true}) // set property instead of attr
el("button", {onclick: myFn}, "Hello") // event handlers
el("button", {onclick: [myFn, arg1, arg2]}, "Hello") // parameterized
el("p", {style: "font-size: 10pt;"}, "Hello") // style can be a string
el("p", {style: {fontSize: "10pt"}}, "Hello") // or an object (camelCase only)
el("div", {style: {width: 35}}, "Hello") // "px" will be added when needed
el("h1", [ // attrs object is optional
el("em", "Important!"),
"foo", 123, // plain values
ie(myElement), // inject existing DOM nodes
el("br"), // void tags without content
"", [], null, undefined, false, // these will be auto-removed
NaN, true, {}, Infinity, // these will be coerced to strings
[ // nested arrays will get flattened
el(".foo", {class: "bar"}, [ // short & attr class get merged: .foo.bar
"Baz",
el("hr"),
])
],
])
el("#ui", [
vw(NavBarView, navbar), // sub-view w/data
vw(PanelView, panel, "panelA"), // sub-view w/data & key
iv(someOtherVM, newData), // injected external ViewModel
])
// special _* props
el("p", {_key: "myParag"}, "Some text") // keyed nodes
el("p", {_data: {foo: 123}}, "Some text") // per-node data (faster than attr)
el("p", {_ref: "myParag"}, "Some text") // named refs (vm.refs.myParag)
el("p", {_ref: "pets.james"}, "Some text") // namespaced (vm.refs.pets.james)
el("p", {_hooks: {willRemove: ...}}, "Some text") // lifecycle hooks
el("div", {_flags: ...}, "Some text") // optimization flags
Spread children
micro+ builds additionally provide two factories for defining child elements using a ...children spread rather than an explicit array.
var el = domvm.defineElementSpread,
sv = domvm.defineSvgElementSpread;
el("ul",
el("li", 1),
el("li", 2),
el("li", 3)
);
JSX
While not all of domvm's features can be accommodated by JSX syntax, it's possible to cover a fairly large subset via a defineElementSpread pragma.
Please refer to demos and examples in the JSX wiki.
Views
What React calls "components", domvm calls "views".
A view definition can be a plain object or a named closure (for isolated working scope, internal view state or helper functions).
The closure must return a template-generating render function or an object containing the same:
Related Skills
node-connect
347.2kDiagnose OpenClaw node connection and pairing failures for Android, iOS, and macOS companion apps
frontend-design
108.0kCreate distinctive, production-grade frontend interfaces with high design quality. Use this skill when the user asks to build web components, pages, or applications. Generates creative, polished code that avoids generic AI aesthetics.
openai-whisper-api
347.2kTranscribe audio via OpenAI Audio Transcriptions API (Whisper).
qqbot-media
347.2kQQBot 富媒体收发能力。使用 <qqmedia> 标签,系统根据文件扩展名自动识别类型(图片/语音/视频/文件)。
