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

staat

v1.3.0-beta.1

Published

A simple state management library

Readme

Staat (Core Library)

A simple state management library.

The goal of this library is to allow simple state management for smaller applications. It is meant to be simple and be typescript friendly.

Staat is not a replacement for Redux, you should weigh which state management solution makes more sense for your project.

This library is loosely inspired by Unstated, another really good option for state management.

Concepts

  • React is supported out of the box, but you can use Staat with other frameworks by using just the core library.
  • The state is unmutable and lives in a container.
  • This state can only be modified by transformers.
  • Transformers are like reducers as in they are plain functions that modify the state.
  • The transformers are called by functions that have their signature based on the trasformer itself, no pubsub is used.
  • Transformers have an input and an output, they receive the current state, the parameters needed to modify that state and return the new state.
  • The functions that call the transformers have a similar signature with the difference that they don't have the current state parameter and they return a promise with the state.
  • Transformers can by async, meaning that you can call functions that do async operations from them, such as api calls.
  • Keep in mind that it's up to you how testable the transformers are.
  • Yes, you can have multiple parameters in your transformer, but in order to make it more readable, using an object as the input is a better option.

Basic usage

import staat from 'staat';

// Setup the initial state.

const initialState = {
  count: 0,
};

// Setup the transformers and pass them to the staat function.

const state = staat(
  {
    add(currentState: typeof initialState, value: number) {
      return { ...currentState, count: currentState.count + value };
    },
    subtract(currentState: typeof initialState, value: number) {
      return { ...currentState, count: currentState.count - value };
    },
  },
  initialState,
);

async function execution() {
  await state.add(10);
  console.log(state.currentState); // { count: 10 }
  await state.subtract(3);
  console.log(state.currentState); // { count: 7 }
}

execution();

State slices

As your app progresses, you might need to have state slices for specific purposes in your application. Staat allows you to have that functionality while still maintaining the single state object.

import staat from 'staat';

// [TS only] Setup the types for the individual state slices.
type CalculatorState = {
  count: number;
};

type UserState = {
  email: string;
};

// [TS only]  Setup the general state.
type AppState = {
  calculator: CalculatorState;
  user: UserState;
};

// Setup the initial state.
const initialState = {
  calculator: {
    count: 0,
  },
  user: {
    email: '',
  },
};

// Setup transformers
const calculator = {
  add(currentState: AppState, value: number) {
    return {
      ...currentState,
      calculator: {
        ...currentState.calculator,
        count: currentState.calculator.count + value,
      },
    };
  },
  subtract(currentState: AppState, value: number) {
    return {
      ...currentState,
      calculator: {
        ...currentState.calculator,
        count: currentState.calculator.count - value,
      },
    };
  },
};

const user = {
  setEmail(currentState: AppState, email: string) {
    return {
      ...currentState,
      user: {
        ...currentState.user,
        email,
      },
    };
  },
};

// Setup the transformers object to pass them to staat.
// The properties here don't have to be the same as the state and you don't need
// to segment the transformers, however it is recommended so the objects are better organized.
const transformers = {
  calculator,
  user,
};

// Pass the initial state and transformers to the staat function.

const state = staat(transformers, initialState);

async function execution() {
  await state.calculator.add(10);
  console.log(state.currentState); // { calculator: { count: 10 }, user: { email: '' } }
  await state.calculator.subtract(3);
  console.log(state.currentState); // { calculator: { count: 7 }, user: { email: '' } }
  await state.user.setEmail('[email protected]');
  console.log(state.currentState); // { calculator: { count: 7 }, user: { email: '[email protected]' } }
}

execution();

Scopes

In the previous example you may have noticed that it can become a bit cumbersome to update slices of the state by modifying the whole state object. In order to solve this issue, you can make use of the scope function to create scoped transformers.

import staat, { scope } from 'staat';

// [TS only] Setup the types for the individual state slices.
type CalculatorState = {
  count: number;
};

type UserState = {
  email: string;
};

// [TS only]  Setup the general state.
type AppState = {
  calculator: CalculatorState;
  user: UserState;
};

// Setup the initial state.
const initialState = {
  calculator: {
    count: 0,
  },
  user: {
    email: '',
  },
};

// Create scopes
// Scopes can go 5 levels deep on Typescript and virtually as deep as you want
// in plain es6.
// Also, the type of the scope is inferred by the property names passed to the function.
const calculatorScope = scope<AppState, 'calculator'>('calculator');
const userScope = scope<AppState, 'user'>('user');

// Setup transformers
const calculator = {
  add: calculatorScope.transformer((currentState, value: number) => {
    return {
      ...currentState,
      count: currentState.count + value,
    };
  }),
  subtract: calculatorScope.transformer((currentState, value: number) => {
    return {
      ...currentState,
      count: currentState.count - value,
    };
  }),
};

const user = {
  setEmail: userScope.transformer((currentState, email: string) => {
    return {
      ...currentState,
      email,
    };
  }),
};

const transformers = {
  calculator,
  user,
};

// Pass the initial state and transformers to the staat function.

const state = staat(transformers, initialState);

// It works the same way as it did before.
async function execution() {
  await state.calculator.add(10);
  console.log(state.currentState); // { calculator: { count: 10 }, user: { email: '' } }
  await state.calculator.subtract(3);
  console.log(state.currentState); // { calculator: { count: 7 }, user: { email: '' } }
  await state.user.setEmail('[email protected]');
  console.log(state.currentState); // { calculator: { count: 7 }, user: { email: '[email protected]' } }
}

execution();

More examples

Check out the example package for a more complex example of how to use staat, including React and Timetravel.