Lightweight React state management with Context API and useSyncExternalStore
npm install react-selective-contextLightweight React state management library built on Context API and useSyncExternalStore. Type-safe, SSR-ready, and optimized for minimal re-renders.
Perfect for managing state in specific sections of your app without prop drilling through multiple component layers.
- ðŠķ Lightweight â Minimal footprint with zero dependencies
- ð Type-safe â Full TypeScript support out of the box
- ⥠Optimized â Shallow comparison prevents unnecessary re-renders
- ð SSR Ready â Built-in support for server-side rendering
- ðŊ Selective Updates â Components only re-render when their selected state changes
- ð§Đ Scoped State â Manage state for specific features or sections, not just global app state
This library shines when you have a feature or section of your app where:
- Multiple components at different nesting levels need access to the same state
- You want to avoid passing props through intermediate components (prop drilling)
- The state is scoped to that feature, not the entire application
Examples:
- A multi-step checkout flow
- A complex form with multiple sections
- A dashboard panel with interconnected widgets
- A modal or drawer with nested interactive components
``bash`
npm install react-selective-context
Let's build a checkout form where multiple nested components need access to cart and shipping data â without passing props through every layer.
`tsx
import {
createSelectiveContext,
SelectiveProvider,
createContextSelector,
createContextSetter,
} from 'react-selective-context'
// Define your feature state type
type CheckoutState = {
items: Array<{ id: string; name: string; price: number; quantity: number }>
shipping: { address: string; city: string; zipCode: string }
promoCode: string | null
}
// Create a typed context for this feature
const CheckoutContext = createSelectiveContext
// Create hooks bound to this context
const useCheckoutSelector = createContextSelector(CheckoutContext)
const useCheckoutSetter = createContextSetter(CheckoutContext)
`
`tsx
function CheckoutPage() {
const initialState: CheckoutState = {
items: [],
shipping: { address: '', city: '', zipCode: '' },
promoCode: null,
}
return (
{/ These components can be deeply nested - no prop drilling needed /}
)
}
`
`tsx
// Deep inside CartSummary component tree
function CartItemCount() {
// Only re-renders when items array changes
const itemCount = useCheckoutSelector((state) =>
state.items.reduce((sum, item) => sum + item.quantity, 0)
)
return {itemCount}
}
// Inside ShippingForm, nested several levels deep
function ShippingAddressPreview() {
// Only re-renders when shipping changes
const shipping = useCheckoutSelector((state) => state.shipping)
return (
{shipping.address}, {shipping.city} {shipping.zipCode}
$3
`tsx
// Deeply nested in the shipping form
function ShippingAddressInput() {
const setCheckoutState = useCheckoutSetter()
const address = useCheckoutSelector((state) => state.shipping.address) const handleChange = (e: React.ChangeEvent) => {
setCheckoutState((prev) => ({
...prev,
shipping: { ...prev.shipping, address: e.target.value },
}))
}
return
}
// In a completely different branch of the component tree
function ApplyPromoButton({ code }: { code: string }) {
const setCheckoutState = useCheckoutSetter()
const applyPromo = () => {
setCheckoutState((prev) => ({ ...prev, promoCode: code }))
}
return
}
`API Reference
$3
Creates a typed React Context for your feature state.
`tsx
const CheckoutContext = createSelectiveContext()
`$3
Provider component that initializes and manages the store for a specific section of your app.
`tsx
`Props:
| Prop | Type | Description |
|------|------|-------------|
|
context | SelectiveContext | The context created by createSelectiveContext |
| initialState | TState | The initial state value |
| children | ReactNode | Child components |$3
Hook to select and subscribe to a slice of state.
`tsx
const shipping = useContextSelector(CheckoutContext, (state) => state.shipping)
`Parameters:
| Parameter | Type | Description |
|-----------|------|-------------|
|
context | SelectiveContext | The state context |
| selector | (state: TState) => TSlice | Function to select a slice of state |
| compare | (a: TSlice, b: TSlice) => boolean | Optional comparison function (defaults to shallow equality) |$3
Hook to get a setter function for updating state.
`tsx
const setState = useContextSetter(CheckoutContext)// Update with a function (recommended)
setState((prev) => ({ ...prev, promoCode: 'SAVE20' }))
// Or update with a new state object
setState({ ...newState })
`$3
Factory function that creates a pre-bound selector hook for a specific context. Recommended for cleaner component code.
`tsx
const useCheckoutSelector = createContextSelector(CheckoutContext)// Later in components:
const items = useCheckoutSelector((state) => state.items)
`$3
Factory function that creates a pre-bound setter hook for a specific context. Recommended for cleaner component code.
`tsx
const useCheckoutSetter = createContextSetter(CheckoutContext)// Later in components:
const setCheckoutState = useCheckoutSetter()
`Advanced Usage
$3
By default,
useContextSelector uses shallow equality to determine if the selected value has changed. You can provide a custom comparison function:`tsx
// Deep equality for complex nested objects
const items = useCheckoutSelector(
(state) => state.items,
(a, b) => JSON.stringify(a) === JSON.stringify(b)
)// Strict equality for primitives
const promoCode = useCheckoutSelector((state) => state.promoCode, Object.is)
`Multiple Contexts for Different Features
You can create separate contexts for different parts of your application. Each feature manages its own state independently:
`tsx
// features/checkout/context.ts
type CheckoutState = { items: CartItem[]; shipping: ShippingInfo }
export const CheckoutContext = createSelectiveContext()
export const useCheckoutSelector = createContextSelector(CheckoutContext)
export const useCheckoutSetter = createContextSetter(CheckoutContext)// features/user-settings/context.ts
type SettingsState = { theme: 'light' | 'dark'; notifications: boolean }
export const SettingsContext = createSelectiveContext()
export const useSettingsSelector = createContextSelector(SettingsContext)
export const useSettingsSetter = createContextSetter(SettingsContext)
// Each feature wraps only its own section
function CheckoutPage() {
return (
)
}
function SettingsPanel() {
return (
)
}
`TypeScript
The library is written in TypeScript and provides full type inference:
`tsx
type CheckoutState = {
items: Array<{ id: string; price: number }>
promoCode: string | null
}const CheckoutContext = createSelectiveContext()
const useCheckoutSelector = createContextSelector(CheckoutContext)
const useCheckoutSetter = createContextSetter(CheckoutContext)
// â
Type-safe selector
const items = useCheckoutSelector((state) => state.items) // type: Array<{ id: string; price: number }>
const promoCode = useCheckoutSelector((state) => state.promoCode) // type: string | null
// â
Type-safe setters
const setState = useCheckoutSetter()
setCheckoutState((prev) => ({ ...prev, promoCode: 'SAVE20' })) // â valid
setCheckoutState((prev) => ({ ...prev, invalid: true })) // â TypeScript error
`How It Works
This library combines React's Context API with
useSyncExternalStore to provide efficient state management:1. Store Creation â When
SelectiveProvider mounts, it creates a store with getState, setState, and subscribe methods
2. Subscription â useContextSelector subscribes to the store using useSyncExternalStore
3. Selective Updates â The selector function extracts only the needed state slice
4. Shallow Comparison â Changes are detected using shallow equality, preventing unnecessary re-renders
5. SSR Support â getServerSnapshot` provides a consistent snapshot during server-side rendering