Clean state management for ReactMore application,for less code.

Expressive MVC moves data, behavior, and lifecycle into a focused model. Components stay small, agent code stays readable, and apps remain easy to build at scale. The goal is fewer lines (and tokens) per feature, and a more pleasant DX.

Drops into React you already have - not a framework, no rewrite.

React has gotten complicated.

useStateuseEffectuseMemouseCallbackuseRefuseContextuseReduceruseLayoutEffectuseIduseTransitionuseStateuseEffectuseMemouseCallbackuseRefuseContextuseReduceruseLayoutEffectuseIduseTransition
useSWRuseQueryuseMutationuseInfiniteQueryuseFormuseFieldArrayuseControlleruseStoreuseSelectoruseDispatchuseSWRuseQueryuseMutationuseInfiniteQueryuseFormuseFieldArrayuseControlleruseStoreuseSelectoruseDispatch
useDeferredValueuseSyncExternalStoreuseImperativeHandleuseOptimisticuseActionStateuseFormStatususeMediaQueryuseDebounceuseVirtualizeruseLocalStorageuseDeferredValueuseSyncExternalStoreuseImperativeHandleuseOptimisticuseActionStateuseFormStatususeMediaQueryuseDebounceuseVirtualizeruseLocalStorage

Hooks accumulate with features. Their logic stays tied to React. Wiring them makes components larger, tightly coupled, and harder to trace.

What Expressive adds

React renders your app.
MVC keeps it organized.

Expressive MVC is a model layer for React. It gives data, async and side effects a home away from display logic. React keeps doing what it's good at, while the logic behind it becomes easier to read, write, and test.

Smaller components

Components focus on rendering instead of coordinating features.

Minimal boilerplate

State classes read like normal JavaScript, even when they power complex logic and UI.

Output you can read

When an agent writes the feature, related logic stays together - easier to extend.

Separation built in

Built-in context keeps models separate without prop drilling, so growth isn't tech debt.

For local state

The same logic. Half the noise.

Say you want a reusable, component-owned useFooBarBaz that re-renders on change. Same surface everywhere - only the cost to build it differs. Higher readability (and fewer tokens).

import React from 'react';import State from '@expressive/react';class FooBarBaz extends State {  foo = 0;  bar = 'hello';  baz = true;  bump() {    this.foo++;  }}function Widget() {  const { foo, bar, baz, bump } = FooBarBaz.use();  return (    <button onClick={bump}>      {foo} · {bar} · {String(baz)}    </button>  );}

With MVC, destructuring is your dependency list. Just read a field to subscribe to it. Nothing to declare like setters, useCallback, or factory.

Classes are their own context.

Wrap a subtree in <Provider for={X}> - components inside need only X.get() to interact with the nearest instance. Fully typed, zero ceremony.

import React from 'react';import State, { Provider } from '@expressive/react';class Theme extends State {  mode = 'light';  toggle() {    this.mode = this.mode === 'light' ? 'dark' : 'light';  }}const Toggle = () => {  const { mode, toggle } = Theme.get();  return <button onClick={toggle}>{mode}</button>;}const App = () => (  <Provider for={Theme}>    <Toggle />  </Provider>);

No createContext<T>, null default, missing-provider guard to write and maintain. Every app needs this eventually. Jotai's Provider will wrap a whole atom store, MobX leaves it to you.

Component is renderable State.

Reach for Component when making self-contained (or extensible) display logic. Fields drive lazy getters and render() directly - destructure this as you would use hook.

A Component is its own Provider too. Children can pull from it with zero prop drilling.

Batteries (and charger) included.

Rails for your React app.

MVC covers stateful behavior you normally need a library for. Build forms, tables, and modals on the same foundation - install a specialist where it earns its place.

With strong fundamentals, you stop reaching for
swrreact-error-boundaryimmeruse-context-selectorformikuse-local-storagereact-queryreact-hook-formusehooks-tsuse-debounce

(Artificial) Idiot-Proof.

Clear conventions mean a good feature looks the same, whether written by you, your team, or an agent. Fewer one-off decisions keep AI focused and on-task.

Dense business logic

State, derived values, async, and lifecycle live together. Composition helps separate concerns into readable chunks.

Type-safe as a rule

Classes pair naturally with TypeScript and JSDoc, so editors surface types and intent where the work is.

Less to trace when things break

No dependency arrays, stale closures, or complicated interactions. A fix starts at the class, not a hunt through wiring.

Class instances are just objects

The instance is the source of truth. Log it, assert on it, or bind it to window to inspect directly.

A layer, not a leap.

Start with the feature already hurting and leave the rest alone. MVC works with the React app and tools you already have.

Incremental adoption

No big-bang rewrite. Adopt it one feature at a time and leave simple useState calls alone. A tool for complexity, not a replacement.

Keep what works

MVC doesn't need to replace every hook or specialist library. Keep the tools that still earn their place.

Portable state

Headless State classes don't depend on a component tree. Move them, test them, or use the framework-agnostic core.

No build-time magic

MVC adds no compiler, code generation, or custom syntax. What you write is what runs.

Move just one feature out of hooks.

Start with one, leave the rest. See how it feels.

The agent skill gives your coding agent the full API and best practices.