little state machinev5GitHub
LITTLE STATE MACHINE · V5

Simple state
for React.

A tiny global store with actions and persistence built in. Share state between components without adding a provider or a lot of setup.

715 B gzipped0 dependenciesReact 18+
Quick startAPI reference
import { createStore, useStateMachine } from 'little-state-machine';

createStore({ count: 0 });

function Counter() {
  const { state, actions } = useStateMachine({
    actions: { increment: state => ({ count: state.count + 1 }) },
  });

  return <button onClick={actions.increment}>{state.count}</button>;
}
UP AND RUNNING

Three lines to
a shared store.

No provider. No ceremony. Just your state, wherever you need it.

Terminal
$ npm install little-state-machine
Then create your store once and start using it in your components.
TRY IT YOURSELF

A little checkout.
A lot less wiring.

Change your details, move through the steps, then refresh the page. The store keeps its state, so your progress is still here.

See how the API works
LIVE DEMO
↻ saved in localStorage
1Your details
2Review
3Done

Where should we send it?

Your changes update the global store as you type.

Little starter kitDigital download · $24
1
$24
Subtotal $24.00
action dispatchedcheckout.updateCheckout
THE API

Small surface.
Room to grow.

Two exports cover the essentials. Start simple, then add selectors and middleware as your app needs them.

01createStore

Set the starting point.

Call once at your app entry. Configure storage, persistence, a name, or middleware.

JavaScript
createStore(
  { user: { name: '' }, cart: [] },
  {
    name: 'my-app',
    storageType: window.localStorage,
    persist: 'action',
  },
);
02useStateMachine

Read state. Dispatch actions.

Use it in any component. Actions receive the current state and a payload, then return the next state.

JavaScript
const updateName = (state, payload) => ({
  ...state,
  user: { ...state.user, ...payload },
});

function Profile() {
  const { state, actions } = useStateMachine({
    actions: { updateName },
  });

  return (
    <button onClick={() => actions.updateName({
      name: 'Ada',
    })}>
      Hello, {state.user.name}
    </button>
  );
}
QUICK REFERENCEEverything you need to know
APIWhat it doesOptions
createStore(state, options?)Initializes the global store.name, middleWares, storageType, persist
useStateMachine(config?)Reads state and returns action dispatchers.actions, selector
actions[name](payload)Runs an action and updates the shared state.Action receives (state, payload)
getState()Reads the current store value imperatively.Returned from useStateMachine
storageType

Choose where state lives.

Pass window.sessionStorage (the default) for this tab, or window.localStorage to persist across browser sessions.

persist

Choose when it saves.

'action' saves after each action. 'beforeUnload' saves when the page closes. 'none' disables persistence.

selector

Subscribe to just a slice.

Return a derived value from your selector to keep a component focused on the state it uses.

middleWares

Observe actions centrally.

Pass middleware functions to inspect or transform store updates as actions run.

STATE SHAPE CHANGES

Persisted state
and migrations

If saved state exists for the store name, createStore loads it as-is. Defaults are only used when there is no saved value, so new keys are not added for existing users. The library has no migration hook.

Version and migrate saved data before calling createStore. Merge nested defaults and explicitly map renamed or remove obsolete fields.

store.ts · v1 to v2
const key = 'my-app';
const storage = window.localStorage;
const defaults = { profile: { name: '', email: '' }, version: 2 };
const saved = JSON.parse(storage.getItem(key) || 'null');

if (saved && (saved.version ?? 1) < 2) {
  const migrated = {
    ...defaults,
    ...saved,
    profile: {
      ...defaults.profile,
      ...saved.profile,
      name: saved.profile?.fullName ?? saved.profile?.name ?? '',
    },
    version: 2,
  };
  delete migrated.profile.fullName;
  delete migrated.legacyField;
  storage.setItem(key, JSON.stringify(migrated));
}

createStore(defaults, { name: key, storageType: storage });
TYPESCRIPT READY

Your state.
Your types.

Augment GlobalState once and your app gets a typed store everywhere.

See the TypeScript guide
global.d.ts
import 'little-state-machine';

declare module 'little-state-machine' {
  interface GlobalState {
    checkout: {
      name: string;
      email: string;
      plan: string;
      quantity: number;
    };
  }
}