Independent widget state
A component subtree needs its own isolated store instance.
Zustand · Const
The context provider pattern supplies a vanilla Zustand store through React context. A custom hook retrieves that store and uses selectors to subscribe components to the state they need.
The context provider pattern creates a vanilla Zustand store inside a React provider, supplies that store through React context, and wraps Zustand's useStore with a custom selector hook. This gives each provider subtree an independent store instance while preserving selective subscriptions.
const StoreContext = createContext<StoreApi<State> | null>(null)
const [store] = useState(() => createStore(initializer))
return <StoreContext.Provider value={store}>{children}</StoreContext.Provider>
useStore(store, selector)import { createContext, useContext, useState, type ReactNode } from 'react'
import { createStore, useStore } from 'zustand'
type CounterState = { count: number; increment: () => void }
const createCounterStore = () => createStore<CounterState>((set) => ({
count: 0,
increment: () => set((state) => ({ count: state.count + 1 })),
}))
const CounterContext = createContext<ReturnType<typeof createCounterStore> | null>(null)
function CounterProvider({ children }: { children: ReactNode }) {
const [store] = useState(createCounterStore)
return <CounterContext.Provider value={store}>{children}</CounterContext.Provider>
}
function useCounter<T>(selector: (state: CounterState) => T) {
const store = useContext(CounterContext)
if (!store) throw new Error('Missing CounterProvider')
return useStore(store, selector)
}
function Counter() {
const count = useCounter((state) => state.count)
const increment = useCounter((state) => state.increment)
return <button onClick={increment}>{count}</button>
}
export function App() {
return <CounterProvider><Counter /></CounterProvider>
}Creates one vanilla store for the provider instance, exposes it through React context, and lets consumers select state with a custom hook.
A component subtree needs its own isolated store instance.
Initial store values or behavior come from provider props.
A Next.js application must avoid sharing state across requests.
Several keyed stores must be made available through one component hierarchy.
Create a vanilla store with createStore, place it in a React context provider, and read it in a custom hook with useContext. The custom hook then calls Zustand's useStore(store, selector) so components subscribe to selected state.
Follow-up: Why create the store inside the provider? It gives each provider subtree an independent store instance and avoids unintentionally sharing state between separate trees.
A provider can initialize its store with useState(() => createStore(...)), which preserves one store instance for that provider across renders. Consumers retrieve the store from context and select only the state they need.
Follow-up: How would you support component props as initial state? Pass those props into the store factory used by the provider, then expose the resulting store through context.
The custom hook should check whether useContext returned a store before calling useStore. If no store exists, it should throw an explicit error stating that the hook must be used within its provider.
Follow-up: What changes for Next.js? Create stores per request, avoid global stores, and do not read or write the store from React Server Components.
createStoreuseStorecreateContextuseContextuseStateinitialize state with props