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

@bazariodev/fsm-react

v1.0.0

Published

React bindings for @bazariodev/fsm sources with useSyncExternalStore subscriptions and selector bail-outs.

Readme

@bazariodev/fsm-react

React bindings for @bazariodev/fsm and any workspace source that exposes the same observation shape: a stable snapshot accessor plus subscribe(listener).

Full design rationale: React.md ADR.

Install

pnpm add @bazariodev/fsm-react @bazariodev/fsm react

react and @bazariodev/fsm are peer dependencies. @bazariodev/fsm is used for shared types only; the runtime import is React.

Usage

import { Fsm } from '@bazariodev/fsm';
import { useFsmSelector, useFsmSnapshot } from '@bazariodev/fsm-react';

const call = new Fsm({
  name: 'call',
  initial: 'idle',
  context: { attempts: 0 },
  states: {
    idle: {},
    dialing: {},
    connected: {},
  },
  transitions: {
    idle: { DIAL: { target: 'dialing' } },
    dialing: { CONNECT: { target: 'connected' } },
    connected: {},
    '*': {},
  },
});

function CallState() {
  const snapshot = useFsmSnapshot(call);
  const attempts = useFsmSelector(call, (s) => s.context.attempts);

  return (
    <button type="button" onClick={() => call.send({ type: 'DIAL' })}>
      {snapshot.value} ({attempts})
    </button>
  );
}

API

import type { FsmSubscribable } from '@bazariodev/fsm';

function useFsmSnapshot<TSnapshot>(
  source: FsmSubscribable<TSnapshot>,
): TSnapshot;

function useFsmSelector<TSnapshot, TSelected>(
  source: FsmSubscribable<TSnapshot>,
  selector: (snapshot: TSnapshot) => TSelected,
  isEqual?: (a: TSelected, b: TSelected) => boolean,
): TSelected;

function useFsm<TMachine extends FsmSubscribable<unknown>>(
  create: () => TMachine,
  options?: {
    attach?: (machine: TMachine) => void | (() => void);
    teardown?: (machine: TMachine) => void;
  },
): Readonly<{ machine: TMachine; snapshot: SnapshotOf<TMachine> }>;

FsmSubscribable is the shared core shape: a stable snapshot accessor plus subscribe(listener) where the listener may receive the committed snapshot.

useFsmSnapshot and useFsmSelector are built on React's useSyncExternalStore, so they are safe for concurrent rendering. Sources must return the same snapshot reference between commits. A source that allocates a fresh object on every snapshot read violates the port contract.

useFsmSelector does not resubscribe when inline selector or isEqual functions change. It does recompute against the current snapshot when those function identities change, so prop-driven selector changes do not return stale data.

Component-owned machines

useFsm is for machines whose lifetime belongs to a component:

import { FsmEffects } from '@bazariodev/fsm-effects';
import { useFsm } from '@bazariodev/fsm-react';

function LocalCall() {
  const { machine, snapshot } = useFsm(() => createCallMachine(), {
    attach: (machine) => {
      const effects = new FsmEffects(machine, {
        effects: {
          dialing: () => {
            // start state-entry work
          },
        },
      });

      return () => effects.stop();
    },
  });

  return (
    <button type="button" onClick={() => machine.send({ type: 'DIAL' })}>
      {snapshot.value}
    </button>
  );
}

React StrictMode can call state initializers twice in development. Because useFsm creates machines in a lazy state initializer, create must be discard-safe: constructor-time hooks such as initial onEnter or hierarchy onNodeSpawned should stay registration-only and resource-free. Acquire sockets, timers, runners, and other external resources in attach, and release them through the returned cleanup or teardown.

If teardown is provided, a machine is considered dead after teardown runs. StrictMode's development effect replay will create a fresh instance instead of reattaching to the dead one, so dev builds may briefly reset component-owned machines to their initial state. Machines that must not reset should live outside the component and be consumed with useFsmSnapshot or useFsmSelector.

Method binding

Fsm.send and FsmHierarchy.send are class prototype methods. Call them as machine.send(event) or wrap them in a handler. Hierarchy node handles are closure-based and can be detached safely.

The hooks wrap source.subscribe and source.snapshot in closures internally, so class-based sources work without consumers binding methods manually.

SSR and RSC

The package passes source.snapshot as getServerSnapshot, so server rendering can produce initial markup. Hydration consistency is the consumer's responsibility: the first client snapshot must match the server-rendered snapshot.

Distributed files include a 'use client' directive banner.

License

MIT