Create a vanilla store from a provider's props and share that store with the React components beneath it.
Use this pattern when a store needs values supplied by a component, such as a scoped feature or an injected dependency. Each mounted provider keeps its own store, and its descendants read and update that store through context.
Create a store from provider props
Install Zustand in your React project:
bashnpm install zustand
- Define the provider's input props and the store state, including its action.
- Create a store factory with
createStorethat combines defaults with the supplied props. The factory returns a vanilla store; the provider keeps that instance in state. - Put the store in React context. The consumer reads it from context and calls
useStoreto select the count and action. - Mount the provider with an initial
bearsvalue. The page renders that count and an Add bear button; clicking the button increments the count shown by the consumer.
tsximport { createRoot } from 'react-dom/client'
import { createContext, useContext, useState } from 'react'
import type { PropsWithChildren } from 'react'
import { createStore, useStore } from 'zustand'
type BearProps = {
bears: number
}
type BearState = BearProps & {
addBear: () => void
}
function createBearStore(initProps?: Partial<BearProps>) {
const defaultProps: BearProps = { bears: 0 }
return createStore<BearState>()((set) => ({
...defaultProps,
...initProps,
addBear: () => set((state) => ({ bears: state.bears + 1 })),
}))
}
type BearStore = ReturnType<typeof createBearStore>
const BearContext = createContext<BearStore | null>(null)
type BearProviderProps = PropsWithChildren<BearProps>
function BearProvider({ children, ...props }: BearProviderProps) {
const [store] = useState(() => createBearStore(props))
return <BearContext.Provider value={store}>{children}</BearContext.Provider>
}
function BearCounter() {
const store = useContext(BearContext)
if (!store) {
throw new Error('BearCounter must be rendered inside BearProvider')
}
const bears = useStore(store, (state) => state.bears)
const addBear = useStore(store, (state) => state.addBear)
return (
<section>
<output aria-live="polite">{bears} Bears.</output>
<button type="button" onClick={addBear}>Add bear</button>
</section>
)
}
function App() {
return (
<BearProvider bears={2}>
<BearCounter />
</BearProvider>
)
}
const container = document.getElementById('root')!
createRoot(container).render(<App />)
Add <div id="root"></div> to the page and load this file from your React entry point. On mount, the consumer displays 2 Bears.; each click on Add bear increases the displayed count by one. The context keeps the store scoped to the provider, while useStore subscribes the consumer to its selected values.
Inputs and behavior
| Input | Type | Default | What it does |
|---|---|---|---|
initProps | Partial<BearProps> | undefined | Supplies initial prop values for the store; the factory's default count is 0, and provided values override it. |
bears | number | Required on BearProvider | Seeds the scoped store in this example. |
useStore selector | (state: BearState) => U | Identity selector if omitted | Returns the selected value so the component can render it or call the selected action. |
The vanilla store is created with createStore, which returns the store API rather than a React hook. The provider holds one created instance for its mounted lifetime, and the context makes that instance available to descendants. useStore returns the selected current-state value and subscribes the component to changes in that selection.
Pitfalls
- Do not use a module-level store when each provider needs independent state; create the store in the provider's initializer as shown.
- In Next.js, avoid sharing a global store across simultaneous server requests, and do not read or write a store from React Server Components. See Set up Zustand in Next.js.
- For middleware caveats about vanilla store API utilities, see Subscribe outside React.
See it running
Open the Zustand live demo to see Zustand running in a browser.
Was this page helpful?