Signal-driven frontend
framework.
Class components with TC39 decorators. Fine-grained signals update only the exact DOM nodes they're bound to — no virtual DOM, no diffing.
Design principles
The ideas behind every decision.
Render once, update precisely
render() runs exactly once on mount. Every subsequent update targets only the DOM nodes that subscribed to a changed signal — nothing more.
Reactivity you can read
@State, @Watch and @Computed sit directly on class fields. Every reactive dependency is a decorator you can see, rename, or delete — no implicit tracking.
No reconciler, no diff
There is no virtual DOM tree to compare. A signal change triggers a direct DOM write on the exact node bound to it. The rest of the page is untouched.
Performance
Close to hand-written DOM.
Measured with js-framework-benchmark against Solid, Svelte, Vue, Lit, Preact and React. Same app, same browser, same machine.
Overall score
Geometric mean of each framework's slowdown vs the fastest entry. 1.00 is the fastest. Lower is better.
- vanillajs1.02
- PraxisJS1.03
- Solid1.14
- Svelte1.18
- Vue1.27
- Lit1.32
- Preact + signals1.37
- React hooks1.78
Striped: vanillajs, hand-written DOM code used as the reference.
Per benchmark
Median total time in ms, click to paint. Lower is better.
| Benchmark | PraxisJS | Solid | Svelte | Vue |
|---|---|---|---|---|
| create 1k | 24.3 | 26.9 | 26.1 | 28.2 |
| update every 10th | 13.6 | 17.5 | 15.7 | 17.6 |
| select row | 3.9 | 4.6 | 7.3 | 5.6 |
| swap rows | 15.7 | 17.3 | 17.9 | 18.3 |
| clear | 12.6 | 14.7 | 14.1 | 17.8 |
js-framework-benchmark, keyed. Chrome 155 , Apple M3 Pro, October 16, 2026. Full results
How it works
Three steps. No magic.
Annotate fields with decorators
@State, @Prop, @Resource — each one wraps a class field in a reactive signal. No store setup, no context providers, no boilerplate.
Wrap expressions in arrow functions
{() => this.count} subscribes that DOM node to the signal. Static reads like {this.count} are safe too — they snapshot the value at render time.
Only subscribed nodes re-evaluate
Assign a new value. The signal notifies its subscribers. Only those DOM nodes update. render() stays idle. No tree traversal, no component re-run.
Quick start
Up and running in minutes.
The CLI scaffolds a complete project — TypeScript, Vite, JSX transform, decorator support, and HMR all pre-configured.
Creates a new project with TypeScript, Vite, JSX transform, and HMR pre-configured.
PraxisJS has zero runtime dependencies. The install is fast.
Vite starts the dev server. Open the browser and start editing your first component.