Skip to content
D
Documentation

Initialize state from props

how-to
2 min readUpdated

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:

bash
npm install zustand
  1. Define the provider's input props and the store state, including its action.
  2. Create a store factory with createStore that combines defaults with the supplied props. The factory returns a vanilla store; the provider keeps that instance in state.
  3. Put the store in React context. The consumer reads it from context and calls useStore to select the count and action.
  4. Mount the provider with an initial bears value. The page renders that count and an Add bear button; clicking the button increments the count shown by the consumer.
tsx
import { 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 />)
The mounted counter shows the initial bear count and the Add bear button.

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

InputTypeDefaultWhat it does
initPropsPartial<BearProps>undefinedSupplies initial prop values for the store; the factory's default count is 0, and provided values override it.
bearsnumberRequired on BearProviderSeeds the scoped store in this example.
useStore selector(state: BearState) => UIdentity selector if omittedReturns 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?