npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2026 – Pkg Stats / Ryan Hefner

@expressive/mvc

v0.83.1

Published

Class-based reactive state management - framework-agnostic core of Expressive MVC.

Readme


The core of Expressive MVC: reactive primitives built around plain classes, with no framework dependency. Provides the State model, instructions, context, and the renderer-agnostic Component that adapters like @expressive/react build on.

Building a React app? Install @expressive/react instead - it depends on this package, so you get the whole core through it and should not list @expressive/mvc in your package.json as well. Import State, Component, and every instruction from the adapter.

Install this package directly only for host-agnostic code that must not depend on a UI adapter - a shared domain package, a Node service, or a new adapter:

npm install @expressive/mvc

State

A State is a class whose fields are reactive - read to subscribe, assign to update. Getters are cached computed values.

import State from '@expressive/mvc';

class Counter extends State {
  count = 0;
  increment = () => this.count++;

  get doubled() {
    return this.count * 2;     // recomputes only when count changes
  }
}

const counter = Counter.new();

// effect runs now, then again whenever read values change
counter.get(({ count }) => {
  console.log(`count is ${count}`);
});

counter.increment();           // -> "count is 1"

Models run and test anywhere - no renderer required.

Instructions

Field initializers that change how a property behaves:

| | | |---|---| | ref() | a mutable reference (e.g. a DOM node) that's still reactive | | set() | computed values, smart setters, side-effects on assignment | | get() | dependency injection - pull another State from context | | map() | a shallow-reactive Map | | has() | an owned reactive collection - an ordered list or a spawned pool |

import State, { set, has, map } from '@expressive/mvc';

class Cart extends State {
  items = has<Item>();                      // reactive collection
  products = map<string, Product>();        // reactive keyed collection
  coupon = set('', code => apply(code));    // side-effect on assignment
}

Lifecycle

class Timer extends State {
  seconds = 0;

  new() {                              // runs once on activation
    const id = setInterval(() => this.seconds++, 1000);
    return () => clearInterval(id);    // returned cleanup runs on destroy
  }
}

const timer = Timer.new();   // construct + activate
timer.set(null);             // destroy - runs cleanup, freezes state

Context & composition

States find each other through context, and compose by holding one another:

import State, { get } from '@expressive/mvc';

class Session extends State {
  user = 'guest';
}

class Profile extends State {
  session = get(Session);    // injected from the nearest provider
  address = new Address();   // nested state - its changes bubble up
}

An adapter (e.g. @expressive/react) supplies the provider that places a State in a tree; get() pulls it back out.

Events

Any property or arbitrary key can be dispatched and listened to:

const off = counter.set('refresh', () => reload());   // listen
counter.set('refresh');                               // dispatch
off();                                                // stop listening

Framework-agnostic components

@expressive/mvc ships its own JSX runtime, so you can author components - including reusable libraries - against the core and let any host render them:

// tsconfig.json
{
  "compilerOptions": {
    "jsx": "react-jsx",
    "jsxImportSource": "@expressive/mvc"
  }
}

Component - a State that renders - lives here as the agnostic base. For using it in an app (rendering, props, subcomponents, suspense), see @expressive/react.


To render state in a UI framework, add an adapter: @expressive/react or @expressive/preact.

Full guide and API reference → github.com/gabeklein/expressive-mvc

License

MIT