Dominator skips the virtual DOM and drives raw DOM nodes from FRP signals
Zero-cost ultra-high-performance declarative DOM library using FRP signals for Rust!
At a glance
- What is it?
- Dominator is a Rust DOM library built on functional reactive programming signals, which keeps raw DOM nodes instead of a virtual DOM and performs O(1) updates however large the tree is. It fits a Rust team compiling to WebAssembly who needs a fast reactive UI, and it does not fit a project that wants a server-rendered or non-browser target.
- Who is it for?
- Dominator fits a Rust team writing a browser application through WebAssembly and willing to work in a signal-based model, and it does not fit a project that renders on the server or needs a JavaScript fallback.
- 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?
- Activity is slowing. The repository last received commits 6 months ago.
- What is it written in?
- Mainly Rust, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on October 22, 2026, and from our analysis. They are not legal advice.
Editorial analysis
No virtual DOM, and that is the entire performance argument
Dominator is described as a zero-cost ultra-high-performance declarative DOM library using FRP signals for Rust, and the mechanism it rejects is the virtual DOM.
The README states that it does not use VDOM, instead it uses raw DOM nodes for maximum performance, that it is close to the metal and has almost no overhead, and that everything is inlined to raw DOM operations. The consequence it claims is that it scales well even with very large applications, because updates are always O(1) time, no matter how big or deeply nested the application is.
That last claim is the one worth understanding as a design constraint rather than a slogan. A virtual DOM approach re-renders a subtree and diffs the result, so the cost of an update is proportional to the size of the changed region. A signal-driven approach propagates a change from the signal to the exact DOM node that depends on it, so the cost is proportional to the number of dependents of that signal, not the size of the tree. Deep nesting stops being a cost.
The benchmark reference in the README is a JavaScript framework benchmark table, and the claim is that Dominator is one of the fastest DOM frameworks in the world, just as fast as Inferno. The README also says the author has successfully used Dominator on multiple large applications and that it performed excellently. Those are the project's own claims, and the benchmark link is where a reader would check them.
The crate is dominator, MIT licensed, by Pauan, at version 0.5.38 with a rust-version of 1.80 and edition 2018.
The API looks like React components but the update model is different
Dominator has a convenient high level declarative API which works similar to React components, but is designed for Rust and FRP signals.
The similarity is at the surface: you describe what you want, and the library works out what to change. The difference is underneath. React re-runs a component function to produce a new description and then reconciles it against the old one. Dominator wires DOM effects to signals, so a value change reaches the node that uses it without re-running the whole tree. The component-shaped API is sugar over that wiring.
The FRP dependency is real and points at a specific body of work. The README links to a Quickbird Studios article on what functional reactive programming is, and the crate depends on futures-channel, futures-util and futures-signals from the futures family, which is where the signal implementation comes from. gloo-events handles the event listeners.
There is a stability claim that matters for adoption. The README says it is generally feature complete, though more convenience methods might be added over time, and that it is quite stable, with breaking changes being very rare and handled with the normal semver system. That is a claim about a 0.x crate, which is where a semver promise does not usually mean much, so it is worth reading the CHANGELOG.md before pinning.
The README also states that it is generally feature complete, which is a useful signal about where the project is going: more convenience, not more architecture.
The dependency list tells you the target is the browser, not a server
The Cargo.toml is the clearest statement of scope in the repository, and it is worth reading line by line.
The runtime dependencies are once_cell, discard, pin-project, futures-channel, futures-util, futures-signals, wasm-bindgen 0.2.48, js-sys, wasm-bindgen-futures, and gloo-events. The web-sys dependency then enables a long list of DOM interfaces: Document, Element, Node, Text, Comment, Event, EventTarget, KeyboardEvent, MouseEvent, PointerEvent, WheelEvent, TouchEvent, DragEvent, FocusEvent, InputEvent, AnimationEvent, History, Location, MediaQueryList, ShadowRoot with its init and mode, StyleSheet and CssRule, CssStyleDeclaration, SvgElement, the HTML element family, and DataTransfer.
That list is the API surface of a browser application, hand-selected. The presence of ShadowRoot and ShadowRootMode, with a cfg check for web_sys_unstable_apis in the lint configuration, suggests shadow DOM work is at least planned. The presence of History and Location suggests routing helpers, and MediaQueryList suggests reactive media queries.
The categories in the manifest are gui, web-programming and wasm, and the keywords list javascript, dom, reactive, signal and frp. The default feature is wasm-bindgen/enable-interning, with a nightly feature listed separately, and the lints section flags the web_sys_unstable_apis check as expected rather than accidental.
There is no server-side rendering path here, no hydration story and no HTML string output. The library assumes a browser and a wasm build, and that assumption is the first thing to check when you are evaluating it.
The examples are the documentation, and one of them is the classic
The repository ships five examples, and the list tells you what the author considers the interesting cases for this model.
There is examples/counter/, which is the smallest possible signal-driven state example. There is examples/todomvc/, which is the canonical test of a reactive UI framework because it has a list, per-item state, derived counts and events that mutate a collection. There is examples/async/, which covers the async side, and since the crate depends on wasm-bindgen-futures that pairing makes sense. There is examples/animation/ and examples/extension/, the latter presumably for the trait-based extension mechanism the feature list hints at when it says more convenience methods might be added.
The running instructions are minimal. Just do yarn and then yarn start, and the README warns it will take a while to compile the dependencies and asks for patience. There is no other setup documentation, which is consistent with a library where the examples are the API reference and the docs.rs badge does the rest.
The community link is a Discord server, and the README invites questions there.
The top level of the repository is small, with .gitignore, CHANGELOG.md, Cargo.toml, LICENSE, README.md, examples/ and src/, which is what a focused library looks like. The lack of a benches/ directory or a separate tooling directory means the performance claims are not reproducible from this repository alone, which is worth knowing before you take the benchmark comparison as settled.
Where Dominator is the wrong tool
The first wrong-tool case is anything that is not a browser application compiled to WebAssembly. There is no server rendering, no static HTML generation and no hydration, so a site that needs to be indexable by a crawler or served without JavaScript is out of scope. A Rust team that wants to render HTML on a server has to reach for a different tool, and the fact that the crate's own categories include gui and wasm rather than web-server tells you where the author aimed it.
The second is a team without appetite for the signal model. The API is React-shaped, so the syntax is approachable, but the mental model underneath is not. A component in Dominator is not a function that returns a description, it is a scope with signals inside it, and debugging a re-render that does not happen or a signal that is read in the wrong place is a different exercise from debugging React.
The third is a JavaScript shop. The framework exists to compile Rust to wasm, and a team that has decided on TypeScript is not going to gain from a Rust library that requires a wasm toolchain in the build. The benchmark comparison in the README is against JavaScript frameworks, but the comparison is about what the Rust output can do once compiled, not about which is easier to adopt.
The fourth is the maturity of the ecosystem. Version 0.5.38 is pre-1.0, the README says breaking changes are very rare and handled with semver, and semver at 0.x means the minor version can break. The CHANGELOG.md is where that is recorded, and a project that depends on it should read it before a version bump rather than after.
Editorial conclusion
Dominator fits a Rust team writing a browser application through WebAssembly and willing to work in a signal-based model, and it does not fit a project that renders on the server or needs a JavaScript fallback. Before adopting it, confirm the target is a browser through wasm-bindgen, since the whole dependency stack is web-sys and wasm-bindgen, and note that the crate is at 0.5.38 with the README stating breaking changes are rare and handled through semver, so a version pin is worth making.
Frequently asked questions
What is Dominator and how is it different from a virtual DOM framework?
Dominator is a Rust DOM library that uses functional reactive programming signals and raw DOM nodes instead of a virtual DOM, so updates are O(1) regardless of how large or deeply nested the application is. The declarative API resembles React components but is built around signals.
What platform does Dominator target?
The browser through WebAssembly. The dependencies are wasm-bindgen, js-sys, wasm-bindgen-futures, gloo-events and web-sys with a long list of DOM interfaces enabled, and the Cargo.toml categories are gui, web-programming and wasm. There is no server-side rendering path.
How do I run the Dominator examples?
Run yarn and then yarn start in the repository. The README notes this takes a while to compile the dependencies and asks for patience. The examples directory holds animation, async, counter, extension and todomvc examples.
Is Dominator stable enough to depend on?
The README says it is quite stable, with breaking changes being very rare and handled with the normal semver system, and that it is generally feature complete. The crate version is 0.5.38, and the Cargo.toml declares a rust-version of 1.80, so the CHANGELOG is worth reading before a version bump.
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/pauan-rust-dominator)Was this useful?