Expressive State
A class-based state backbone for modern UI applications
Expressive State is a reactive state library built around plain classes. It's designed to be the backbone of your application - the place where data, behavior, and lifecycle live - so that components can go back to doing what components are best at: describing UI.
import State from '@expressive/react';
class Counter extends State {
count = 0;
increment() {
this.count++;
}
decrement() {
this.count--;
}
}
function CounterWidget() {
const { count, increment, decrement } = Counter.use();
return (
<div>
<button onClick={decrement}>-</button>
<span>{count}</span>
<button onClick={increment}>+</button>
</div>
);
}No reducers. No selectors. No dependency arrays. No extra libraries for async, context, or computed values. Just a class and a hook.
Why it exists
Most React apps outgrow their state. What starts as useState sprawls into useEffect chains, useCallback memoization, custom hooks that return objects of hooks, then a store library, then middleware for async, then a query client. By the time a feature is "done", its logic is scattered across five different primitives - none of which are easy to test, reuse, or understand in isolation.
Expressive takes a different approach: put the logic back in one place. A State class holds everything a feature knows and does. Components subscribe to what they need and render it. The two concerns - data and presentation - stop fighting each other.
What you get from a single import
- Reactive properties - plain class fields that components subscribe to automatically.
- Computed values - derived properties that update when their inputs change.
- Async + Suspense - declarative data loading with built-in Suspense integration.
- Context - type-safe dependency injection; the class is the context key.
- Lifecycle - one
new()hook for setup and teardown. No dependency arrays. - Components - the
Componentclass lets state render itself, with built-in error boundaries and subcomponents. - Testability - state classes are plain objects. Test them without a renderer, without
act(), without a DOM.