Class-based state for ReactStructure withoutall the baggage.

Expressive MVC moves data, behavior, and lifecycle into focused classes. Components stay small, application logic stays readable, and every feature takes less code to build, test, and change.

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

1,457 tests · 100% coverage · 11.4 kB min+gzip · v0.91.0

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 difficult to trace.

What Expressive adds

React renders your app.
MVC keeps it organized.

Expressive MVC is a model layer for your app. It gives data, async and side effects a home away from display logic. Components stay simple and behavior easy to read, write and debug.

For local state

The same logic. Half the noise.

Say you want a reusable, component-owned useFooBarBaz that re-renders on change. Examples have same state and behavior. Expressive gets there with less code.

With MVC, destructuring is your dependency list. Just read a field to subscribe to it. No setters, dependency arrays, or memoized callbacks.

Vibe code you can keep.

Clear conventions mean a good feature looks the same, whether written by you, your team, or an agent. Generated code stays readable, with less to trace when things break.

Classes are their own context.

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

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 a Model with a view.

Component extends Model for self-contained (or extensible) display logic. It's renderable in JSX. Fields drive getters and render() method directly - destructure this as you would use hook.

A Component provides itself too, accessible to children without prop drilling.

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.

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.