706 bytes minified · zero dependencies

Finite State Machines,
without the weight.

@tahaghafuri/tinyfsm gives you predictable state logic in any JavaScript or TypeScript project. No classes. No decorators. No runtime overhead.

Get started ★ GitHub
706Bminified
0dependencies
100%TypeScript
live demo — traffic light waiting for input…
state: red
Stop — waiting for green
INSTALL Get started in seconds
npm install @tahaghafuri/tinyfsm
pnpm add @tahaghafuri/tinyfsm
yarn add @tahaghafuri/tinyfsm
https://cdn.jsdelivr.net/npm/@tahaghafuri/tinyfsm/dist/tinyfsm.umd.min.js
https://cdnjs.cloudflare.com/ajax/libs/@tahaghafuri/tinyfsm/1.0.0/tinyfsm.umd.min.js
USAGE Fetch machine — the most common pattern
import { createMachine } from '@tahaghafuri/tinyfsm';

const fetcher = createMachine({
  initial: 'idle',
  states: {
    idle:    { on: { FETCH:   'loading' } },
    loading: { on: { RESOLVE: 'success', REJECT: 'error' } },
    success: { on: { RESET:   'idle' } },
    error:   { on: { RETRY:   'loading', RESET: 'idle' } },
  },
});

fetcher.send('FETCH');          // → loading
fetcher.send('RESOLVE');       // → success
fetcher.is('success');        // true

// Subscribe to changes
const unsub = fetcher.on((next, prev) => {
  console.log(`${prev} → ${next}`);
});

// Unsubscribe when done
unsub();

With lifecycle hooks
const door = createMachine({
  initial: 'closed',
  states: {
    closed: {
      on: { OPEN: 'open' },
      onExit: (to) => console.log(`Leaving closed → ${to}`),
    },
    open: {
      on: { CLOSE: 'closed' },
      onEnter: (from) => console.log(`Entered open from ${from}`),
    },
  },
});

door.send('OPEN');
// Leaving closed → open
// Entered open from closed

TypeScript — fully typed
type State = 'idle' | 'loading' | 'success' | 'error';
type Event = 'FETCH' | 'RESOLVE' | 'REJECT' | 'RESET';

const machine = createMachine<State, Event>({
  initial: 'idle',
  states: { /* ... */ },
});

// TS error if you use an invalid state or event ✓
API createMachine(options) → FSM
MethodReturnsDescription
.state S Current state (readonly getter)
.send(event) boolean Send an event. Returns true if a transition happened
.on(listener) () => void Subscribe to state changes. Returns an unsubscribe function
.can(event) boolean Check if an event triggers a transition from current state
.is(state) boolean Check if current state matches given state