Signal Ctx
A tiny, signal-based state utility for React that solves the useContext re-render problem using useSyncExternalStore.
Tech Stack
✨ Features
- ⚡ Signal-style state container
- 🎯 Selector-based subscriptions
- 🧵 React 18 concurrent-safe
- 🧩 Context-backed but not context-driven
- 📦 Very small bundle size
- 🌳 Tree-shakable
- 🧠 Explicit and predictable
📦 Installation
Peer dependency: React 18+
🧠 Core Idea
Context does not store state.
It stores a stable signal reference.
The state lives outside React, and components subscribe directly to the signal.
🔹 Signal
A signal is:
- A function that returns state
- Can be subscribed to
- Can be updated imperatively
🔹 Low-Level Functions
newSignal(init)
Creates a low-level signal.
🔹 React Hooks
useValue(store, selector?)
Subscribe to a signal.
- Uses
useSyncExternalStore - Re-renders only when the selected value changes
- Selector is optional
useSet(store, selector?)
Returns a setter function.
Scoped update:
⚠️ Updates are mutation-based. Spread manually if you want immutability.
🔹 Context-Based API
createCtx(init)
Creates a context-backed signal store hook.
The returned function has these properties:
useAppCtx(selector, options)- Hook to select stateuseAppCtx.Provider- Context provider componentuseAppCtx.useSet(selector, options)- Hook to get setter functionuseAppCtx.useSignal(options)- Hook to access raw signal underlying the context
🚀 Usage
1. Create a Provider
2. Read only what you need
3. Update state
4. Custom signal for additional logic
✅ Updating count does NOT re-render Book.
🧩 Why This Works
- Context value never changes
- React does not re-render on context updates
useSyncExternalStorecompares selected snapshots- Only changed selectors trigger re-renders
This is the same model used by:
- Redux
useSelector - Zustand selectors
- React’s official external store docs
⚠️ Important Rule
Never destructure the entire state. Always select the smallest possible slice.
❌ Bad:
✅ Good:
🧩 Multiple Stores
You can create isolated stores using name.
Usage
Each store is independent.
🌐 Server-Side Rendering (SSR)
Signal Ctx is SSR-safe.
- Uses
useSyncExternalStore - Identical snapshot logic on server & client
- No shared global state between requests
⚠️ Caveats
- No middleware
- No devtools
- No persistence
- Mutation-based updates by design
Best suited for:
- UI state
- Lightweight global stores
- flexible shared state
🧪 TypeScript
Fully typed with generics and inferred selectors.
📄 License
MIT
⭐ Philosophy
signalctx is intentionally small.
It favors:
- Explicit ownership
- Predictable updates
- Minimal abstraction
If you understand React, you understand signalctx.