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

@forgedevstack/synapse

v1.2.0

Published

Synapse - Ultra-simple state management for React. No dispatch, no reducers, just signals.

Readme

Synapse

Ultra-simple state management for React. No dispatch, no reducers, no selectors. Create a nucleus and use it in your components.

npm version bundle size license


No dispatch. No reducers. No selectors. No boilerplate.

Create state in one line. Use it everywhere. No provider required.


What is Synapse?

Synapse is a small state library for React. You define state and actions in a single place (a "nucleus"), then read and update that state from any component with hooks. It is designed to be minimal: no actions, no reducers, no store provider. You call set() with partial state or an updater function, and components that use that state re-render.

Synapse fits apps that want React-friendly state without Redux or heavy setup. It supports batching, persistence, time-travel debugging via the Chrome DevTools extension, and optional signals for fine-grained updates.


Why Synapse?

  • Simple API — One function to create state (createNucleus), one hook to use it (useNucleus). Optional selector for slices.
  • Small — Under 2 KB gzipped.
  • Fast — Batched updates and shallow equality in selectors reduce unnecessary re-renders.
  • TypeScript — Typed out of the box.
  • DevTools — Chrome extension: inspect nuclei, time-travel, export/import state, view API call list with request/response and headers.
  • Middleware — Logger, persist, undo, throttle, debounce, validate, cross-tab sync, immer-style updates.
  • Testing — Helpers in @forgedevstack/synapse/testing: createTestNucleus, waitForState, collectSnapshots.

Installation

npm i @forgedevstack/synapse
# or
yarn add @forgedevstack/synapse
# or
pnpm add @forgedevstack/synapse

Requires React 16.8+ (hooks).


Quick Start

1. Create a Nucleus (state container)

import { createNucleus } from '@forgedevstack/synapse';

interface UserState {
  user: { name: string; email: string } | null;
  loading: boolean;
  login: (email: string, password: string) => Promise<void>;
  logout: () => void;
}

export const userNucleus = createNucleus<UserState>((set) => ({
  user: null,
  loading: false,

  login: async (email, password) => {
    set({ loading: true });
    const user = await api.login(email, password);
    set({ user, loading: false });
  },

  logout: () => set({ user: null }),
}));

2. Use in components

import { useNucleus, usePick, useNucleusSlice } from '@forgedevstack/synapse';

// Full state
function UserProfile() {
  const { user, logout } = useNucleus(userNucleus);
  if (!user) return <LoginForm />;

  return (
    <div>
      <h1>Welcome, {user.name}</h1>
      <button onClick={logout}>Logout</button>
    </div>
  );
}

// Selector: re-renders only when name changes
function UserName() {
  const name = useNucleus(userNucleus, (s) => s.user?.name);
  return <span>{name}</span>;
}

// Multiple keys in one subscription
function LoginButton() {
  const { loading, login } = useNucleusSlice(userNucleus, ['loading', 'login']);
  return <button disabled={loading} onClick={() => login('a', 'b')}>Log in</button>;
}

3. Batch updates

import { batchUpdates } from '@forgedevstack/synapse';

batchUpdates(() => {
  myNucleus.set({ a: 1 });
  myNucleus.set({ b: 2 });
});
// Listeners fire once with { a: 1, b: 2 }

Core concepts

Nucleus vs store

In Synapse, state lives in a nucleus. You create it with createNucleus(initializer). The initializer receives set, get, and the nucleus; you return the initial state (including functions that call set). There is no separate "dispatch" or "actions" object.

| Redux / Zustand | Synapse | |-----------------|---------| | store | nucleus | | dispatch(action)| set({ ... }) | | useSelector() | usePick() / useNucleus(n, selector) | | createStore() | createNucleus() |

Signals (optional)

For local or shared primitive state you can use signals: signal(0), computed(() => ...), and hooks useSignal, useComputed. Signals work alongside nuclei.


DevTools

Install the Synapse DevTools Chrome extension to:

  • Inspect all nuclei and their state
  • Time-travel through action history (and use the time slider)
  • Filter by nucleus (store tabs in the top bar)
  • Export and import state for debugging
  • View API calls: list of fetch/XHR with method, URL, status, duration; click a call to see request and response headers and body (RTK-style)

Load the extension from synapse/devtools-extension/dist as an unpacked extension in chrome://extensions. Open DevTools and select the "Synapse" tab.


Middleware

Import from @forgedevstack/synapse/middleware:

  • logger — Log state changes (with optional diff and timestamp).
  • persist — Save and restore state to localStorage/sessionStorage; supports version and migrate.
  • immer — Use mutable-style updates inside set(fn) that are applied immutably.
  • undo — Undo/redo with a history limit.
  • throttle / debounce — Throttle or debounce updates (optionally by state keys).
  • validate — Validate state with a Zod-like schema (reject, warn, or fix).
  • sync — Cross-tab state sync via BroadcastChannel.
  • subscribeWithSelector — Add subscribeWithSelector(selector, listener) to the nucleus for selector-based subscriptions.

API overview

Core: createNucleus, batchUpdates, signal, computed, batch, effect

Hooks: useNucleus, usePick, useNucleusSlice, useNuclei, useAction, useSubscribe, useSnapshot, useSignal, useComputed, useQuery, useMutation

Testing: createTestNucleus, waitForState, collectSnapshots from @forgedevstack/synapse/testing

See the full API Reference table in this README and the repository for more detail.


API Reference

Core

| Function | Description | |----------|-------------| | createNucleus(init, config?) | Create a new nucleus | | batchUpdates(fn) | Batch synchronous updates; listeners fire once | | signal(value, name?) | Create a reactive signal | | computed(fn) | Create a derived signal | | batch(fn) | Batch signal updates | | effect(fn) | Run side effects on signal changes |

Hooks

| Hook | Description | |------|-------------| | useNucleus(n) | Use entire nucleus state | | useNucleus(n, selector) | Use a slice (optimised re-renders) | | usePick(n, selector, eq?) | Select state with custom equality (default shallow) | | useNucleusSlice(n, keys) | Pick multiple keys in one subscription | | useNuclei([...]) | Use multiple nuclei | | useAction(fn) | Stable action reference | | useSubscribe(n, cb) | Subscribe to changes (no re-render) | | useSnapshot(n) | Get state getter (no re-render) | | useSignal(sig) | Use signal value | | useComputed(comp) | Use computed value | | useQuery(fetcher, opts?) | Fetch data with state | | useMutation(fn, opts?) | Handle mutations |

Middleware

| Middleware | Description | |------------|-------------| | logger(opts?) | Log state changes | | persist(opts) | Save / restore state (sync and async storage, versioned migrations) | | immer() | Mutable-style immutable updates | | undo(opts?) | Undo / redo with history | | throttle(opts?) | Throttle state updates | | debounce(opts?) | Debounce state updates | | validate(opts) | Validate state with Zod-like schemas | | sync(opts) | Cross-tab state sync via BroadcastChannel | | subscribeWithSelector() | Selector-based external subscriptions |

Testing (@forgedevstack/synapse/testing)

| Utility | Description | |---------|-------------| | createTestNucleus(init, overrides?) | Nucleus with devtools and persist disabled | | waitForState(n, predicate, opts?) | Await a state condition | | collectSnapshots(n) | Record emitted states |


Comparison

| Feature | Synapse | Redux | Zustand | Jotai | |---------|---------|-------|---------|-------| | Bundle size | ~2 KB | ~7 KB | ~1 KB | ~3 KB | | Boilerplate | Minimal | Heavy | Low | Low | | TypeScript | Native | Needs setup | Good | Good | | DevTools | Chrome extension | Extension | Extension | Extension | | Async actions | Native | Middleware | Native | Native | | Persistence | Middleware | Manual | Manual | Plugin | | Undo / Redo | Middleware | Manual | Manual | — | | Cross-tab sync | Middleware | — | — | — | | Testing utils | synapse/testing | — | — | — | | Learning curve | Easy | Steep | Easy | Medium |


License

MIT © John Yaghobieh

Part of the ForgeStack ecosystem.