Wabou is an experimental native UI runtime for building desktop applications with SolidJS and Rust. It runs application logic in QuickJS, then performs layout, painting, and native integration in Rust—without embedding a browser or a WebView.
Wabou is under active development. The architecture is usable for experimentation, but the public API and platform support are not stable yet.
An interactive catalogue of Wabou components and platform features, including animations, native windows, and Rust-powered custom widgets.
A native terminal widget powered by rio-vt, demonstrating keyboard and pointer input, text selection, clipboard integration, scrolling, and system font discovery.
Web UI is productive, but a browser engine is a large runtime to ship when an application only needs a reactive component model. Traditional native UI can be lean and fast, but often gives up the component ergonomics and iteration speed frontend developers expect.
Wabou explores a narrower combination:
- SolidJS signals and JSX for application state and composition;
- QuickJS as a small, embeddable JavaScript runtime;
- a compact binary protocol instead of a DOM or JSON bridge;
- retained layout with Taffy and GPU rendering with Vello;
- Rust for windows, input, text, native widgets, and platform integration;
- Vite HMR and an inspector for a short development loop.
Solid fits an embedded runtime particularly well because its reactivity is fine-grained. A signal update runs the computations that depend on it; it does not require rebuilding and diffing a virtual component tree every frame. That keeps both JavaScript work and bridge traffic proportional to what changed.
Wabou implements Solid's universal renderer rather than emulating a browser DOM. Solid owns signals, effects, component lifetimes, and reconciliation, while the Wabou host emits only the mutations needed by the retained Rust tree. This also lets state stay explicit in Solid primitives: styling does not create a second, implicit state machine through CSS pseudo-classes.
The result is a familiar declarative model with a deliberately small boundary between JavaScript and native code.
Solid signals + JSX
│ changed nodes only
▼
Solid universal renderer
│ compact binary operations
▼
QuickJS ───────────────► Rust host ──► Taffy layout ──► Vello / native widgets
▲ │
└──── input, timers, and host events ───────────────┘
Wabou does not make QuickJS render pixels, and “120 fps” is not an unconditional benchmark claim. The display refresh rate supplies the cadence; Wabou arranges the work so JavaScript does not have to be the whole frame.
For every active display frame:
- The Rust host delivers pending input and host messages.
- It runs one
requestAnimationFrameturn in QuickJS with the frame timestamp. - Solid updates enqueue binary mutations, which are flushed once at the end of that turn.
- Rust applies the mutations to its retained tree, recomputes only invalidated work, and lays out and paints at vsync.
On a 60 Hz display, all frame work must fit in roughly 16.67 ms. On a 120 Hz display, it must fit in roughly 8.33 ms. Solid's targeted updates, batched binary transport, retained native state, and Rust-side layout/rendering are how Wabou makes those budgets attainable. Application code can still miss them: long JavaScript callbacks, excessive layout invalidation, expensive painting, or too many visible nodes will drop frames. Large collections should be windowed, and animation work should stay small and measurable.
Continuous redraw is demand-driven. A queued requestAnimationFrame keeps the
host drawing at the display cadence; when no animation is active, Wabou returns
to event-driven rendering instead of spending CPU producing identical frames.
Async jobs also have a bounded scheduler budget so an unbounded microtask chain
cannot monopolize a UI callback.
Use the DevTools inspector to observe QuickJS tick, frame build, scene, and presentation timing. Performance claims should be accompanied by a reproducible workload, hardware, operating system, scale factor, and refresh rate.
You need a current Rust toolchain and mise. Clone the repository, then run:
mise install
mise exec -- bun install
mise exec -- bun run wabou run apps/galleryUntil binary releases are available, the CLI can also be installed directly from the repository (the checkout workflow above is still recommended for contributing):
cargo install --git https://github.com/SunDoge/wabou.git wabou-cliRust application code should depend on the wabou facade instead of its
implementation crates. For a developer preview, pin the dependency to the
preview tag you are testing:
[dependencies]
wabou = { git = "https://github.com/SunDoge/wabou.git", tag = "v0.1.0-alpha.1" }Other examples include a Hacker News client and a terminal. See the CLI guide for development and packaging commands.
- Explicit state. Interaction state belongs in Solid signals and props, not hidden CSS state machines.
- Pay for changes. Batch mutations and preserve layout and render state across frames.
- Idle means idle. Redraw continuously only while animation requests it.
- Native where it matters. Keep platform access and performance-sensitive work in Rust without making every UI change a Rust compile cycle.
- Measure the real layer. A successful build is not evidence of smooth rendering; verify frame timing and output on the target platform and display.
Read more about styling, windows, accessibility, behavior testing, performance profiling, JavaScript packages, native widgets, host-to-JavaScript communication, and composable events. Public API naming and embedded host testing are documented in the JavaScript API design guide. The supported browser-like surface is defined by the Web compatibility contract; the private bridge is listed in the generated Host ABI inventory. Tested runtime packages are tracked in the JavaScript library compatibility list.
Wabou is looking for contributors interested in native rendering, SolidJS integration, developer tooling, accessibility, text, and cross-platform UI. The most useful contributions today are focused bug reports with a reproduction, small examples that stress a real application pattern, and improvements that make the public API smaller and more predictable.
See CONTRIBUTING.md for setup, checks, and commit conventions. If you are evaluating Wabou for an application, open an issue describing the use case and the missing capability; real constraints are more valuable than a generic feature wishlist.
This project was deeply inspired by PocketJS. Their pioneering work in Rust-based minimalistic UI rendering and architectural design significantly influenced the direction of wabou.
Wabou is licensed under the Apache License 2.0.

