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.
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.