Synapse - Ultra-simple state management for React. No dispatch, no reducers, just signals.
npm install @forgedevstack/synapse> Ultra-simple state management for React. No dispatch, no reducers, just signals.



State management shouldn't be complicated. Synapse makes it as simple as:
``tsx
// Create your state
const counterNucleus = createNucleus((set) => ({
count: 0,
increment: () => set((s) => ({ count: s.count + 1 })),
decrement: () => set((s) => ({ count: s.count - 1 })),
}));
// Use in components
function Counter() {
const { count, increment, decrement } = useNucleus(counterNucleus);
return ;
}
`
No dispatch. No reducers. No selectors. No boilerplate.
- Ultra-simple API - Create state in seconds
- Tiny bundle - < 2KB gzipped
- Fast - Minimal re-renders with fine-grained subscriptions
- DevTools - Chrome/Safari extension for debugging
- TypeScript - Full type safety out of the box
- Middleware - Logger, persist, immer-like updates
- API integration - Built-in hooks for data fetching
- Time travel - Debug with state history
- React 16.8+ - Works with all modern React versions
`bash`
npm i @forgedevstack/synapseor
yarn add @forgedevstack/synapseor
pnpm add @forgedevstack/synapse
`tsx
import { createNucleus } from '@forgedevstack/synapse';
interface UserState {
user: { name: string; email: string } | null;
loading: boolean;
login: (email: string, password: string) => Promise
logout: () => void;
}
export const userNucleus = createNucleus
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 }),
}));
`
`tsx
import { useNucleus, usePick } from '@forgedevstack/synapse';
import { userNucleus } from './state/user';
// Use entire state
function UserProfile() {
const { user, logout } = useNucleus(userNucleus);
if (!user) return
return (
// Or pick specific values (optimized re-renders)
function UserName() {
const name = usePick(userNucleus, (s) => s.user?.name);
return {name};
}
`
In Synapse, we use Nucleus instead of "store". A nucleus is the core of your state - it holds the state and provides methods to update it.
| Redux/Zustand | Synapse |
|--------------|---------|
| store | nucleus |dispatch(action)
| | set({ ... }) |useSelector()
| | usePick() |createStore()
| | createNucleus() |
For simpler state, use signals:
`tsx
import { signal, useSignal } from '@forgedevstack/synapse';
// Create a signal
const count = signal(0);
// Use in component
function Counter() {
const value = useSignal(count);
return (
);
}
`
Derive values from signals:
`tsx
import { signal, computed, useComputed } from '@forgedevstack/synapse';
const firstName = signal('John');
const lastName = signal('Doe');
const fullName = computed(() =>
${firstName.value} ${lastName.value}
);
function Name() {
const name = useComputed(fullName);
return
Middleware
$3
`tsx
import { createNucleus } from '@forgedevstack/synapse';
import { logger } from '@forgedevstack/synapse/middleware';const myNucleus = createNucleus(
(set) => ({ count: 0 }),
{
middleware: [
logger({ diff: true, timestamp: true })
]
}
);
`$3
`tsx
import { persist } from '@forgedevstack/synapse/middleware';const userNucleus = createNucleus(
(set) => ({
name: '',
email: '',
preferences: { theme: 'dark' },
}),
{
middleware: [
persist({
key: 'user-state',
storage: 'local', // 'local' | 'session' | custom
include: ['preferences'], // Only persist preferences
})
]
}
);
`$3
`tsx
import { immer } from '@forgedevstack/synapse/middleware';const todosNucleus = createNucleus(
(set) => ({
todos: [{ id: 1, text: 'Learn Synapse', done: false }],
toggle: (id: number) => set((draft) => {
// Looks mutable, works immutably!
const todo = draft.todos.find(t => t.id === id);
if (todo) todo.done = !todo.done;
}),
}),
{ middleware: [immer()] }
);
`API Hooks
$3
`tsx
import { useQuery } from '@forgedevstack/synapse';function UserList() {
const { data, loading, error, refetch } = useQuery(
() => fetch('/api/users').then(r => r.json()),
{ refetchOnFocus: true }
);
if (loading) return ;
if (error) return ;
return (
{data?.map(user => - {user.name}
)}
);
}
`$3
`tsx
import { useMutation } from '@forgedevstack/synapse';function CreateUser() {
const { mutate, loading } = useMutation(
(data) => fetch('/api/users', {
method: 'POST',
body: JSON.stringify(data),
}).then(r => r.json()),
{
onSuccess: (user) => navigate(
/users/${user.id}),
}
);
return (
);
}
`DevTools
Install the Synapse DevTools browser extension to:
- Inspect all nuclei in your app
- Time-travel through state history
- Export/import state for debugging
- Reset state to initial values
Download for Chrome | Download for Safari
Configuration
$3
`tsx
import { createNucleus, type SynapseConfig } from '@forgedevstack/synapse';const config: SynapseConfig = {
// Action naming convention
actionNaming: 'camelCase', // 'camelCase' | 'PascalCase' | 'snake_case' | 'SCREAMING_SNAKE_CASE'
// DevTools (auto-enabled in development)
devtools: true,
devtoolsName: 'MyApp',
// Enable action logging
logging: process.env.NODE_ENV === 'development',
// Persist state
persist: {
key: 'app-state',
storage: 'local',
},
};
const appNucleus = createNucleus((set) => ({
// state...
}), config);
`TypeScript
Synapse is written in TypeScript and provides excellent type inference:
`tsx
interface Todo {
id: number;
text: string;
done: boolean;
}interface TodosState {
todos: Todo[];
filter: 'all' | 'active' | 'done';
addTodo: (text: string) => void;
toggleTodo: (id: number) => void;
setFilter: (filter: 'all' | 'active' | 'done') => void;
}
const todosNucleus = createNucleus((set) => ({
todos: [],
filter: 'all',
addTodo: (text) => set((state) => ({
todos: [...state.todos, { id: Date.now(), text, done: false }],
})),
toggleTodo: (id) => set((state) => ({
todos: state.todos.map(t =>
t.id === id ? { ...t, done: !t.done } : t
),
})),
setFilter: (filter) => set({ filter }),
}));
`API Reference
$3
| Function | Description |
|----------|-------------|
|
createNucleus(initializer, config?) | Create a new nucleus |
| signal(initialValue) | Create a reactive signal |
| computed(computeFn) | Create a derived signal |
| batch(fn) | Batch multiple updates |
| effect(fn) | Run side effects on signal changes |$3
| Hook | Description |
|------|-------------|
|
useNucleus(nucleus) | Use entire nucleus state |
| usePick(nucleus, selector) | Use selected state slice |
| useNuclei([...nuclei]) | Use multiple nuclei |
| useSignal(signal) | Use signal value |
| useComputed(computed) | Use computed value |
| useQuery(fetcher, options?) | Fetch data with state |
| useMutation(mutationFn, options?) | Handle mutations |
| useSubscribe(nucleus, callback) | Subscribe to changes |
| useSnapshot(nucleus) | Get state without subscribing |$3
| Middleware | Description |
|------------|-------------|
|
logger(options?) | Log state changes |
| persist(options) | Persist state to storage |
| immer()` | Enable mutable-style updates || Feature | Synapse | Redux | Zustand | Jotai |
|---------|---------|-------|---------|-------|
| Bundle size | ~2KB | ~7KB | ~1KB | ~3KB |
| Boilerplate | Minimal | Heavy | Low | Low |
| TypeScript | Native | Needs setup | Good | Good |
| DevTools | Built-in | Extension | Extension | Extension |
| Async actions | Native | Middleware | Native | Native |
| Learning curve | Easy | Steep | Easy | Medium |
MIT © John Yaghobieh
---
Part of the ForgeStack ecosystem.