# Vanilla and scoped stores

For application-wide state, use the hook form returned by [`create`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#create). Use [`createStore`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#createstore) when creation must be separate from React, or when each scope needs its own store instance.

Install the [`zustand`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand) package with `npm install zustand`. Its `zustand/vanilla` entry provides the standalone store creator, while its React entry provides the binding hook.

## How the parts fit together

```mermaid
flowchart LR
  F["store factory"] --> V["vanilla store API"]
  V --> H["React store binding"]
  H --> C["React component"]
  P["React context"] --> H
  R["request boundary"] --> F
```

`createStore` returns a standalone store API. Its `getState`, `setState`, `subscribe`, and `getInitialState` methods let non-React code use the store. Pass that store and a selector to [`useStore`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#usestore) in a component; the component then subscribes to the selected value.

Use a module-level store when the state is genuinely global. Use a factory when initial values come from component props, when two parts of the tree must not share state, or when a server must create one store per request. Context carries the selected store instance to the component subtree; it does not replace the store API.

For the direct bound-hook pattern, including atomic selections and the rendered result, see [Selectors and rendering](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/selectors-and-rendering).

## Scope a store through context

Use a factory and context when each provider instance needs independent state or receives initial props. Keep the store in `useState` so a provider re-render does not create a new store.

When this scoped binding needs a custom equality function, install the `zustand/traditional` peer dependency with `npm install zustand react use-sync-external-store` and use [`useStoreWithEqualityFn`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand-traditional#usestorewithequalityfn) in the context hook.

```tsx
import {
  createContext,
  useContext,
  useState,
  type PropsWithChildren,
  type ReactNode,
} from 'react'
import { createStore } from 'zustand/vanilla'
import { useStoreWithEqualityFn } from 'zustand/traditional'

type CounterProps = {
  count: number
}

type CounterState = CounterProps & {
  increment: () => void
}

const createCounterStore = (props: Partial<CounterProps> = {}) =>
  createStore<CounterState>()((set) => ({
    count: props.count ?? 0,
    increment: () => set((state) => ({ count: state.count + 1 })),
  }))

type CounterStore = ReturnType<typeof createCounterStore>

const CounterContext = createContext<CounterStore | null>(null)

type CounterProviderProps = PropsWithChildren<CounterProps>

export function CounterProvider({
  children,
  ...props
}: CounterProviderProps): ReactNode {
  const [store] = useState(() => createCounterStore(props))
  return (
    <CounterContext.Provider value={store}>
      {children}
    </CounterContext.Provider>
  )
}

function useCounterStore<T>(selector: (state: CounterState) => T): T {
  const store = useContext(CounterContext)
  if (!store) {
    throw new Error('CounterProvider is missing')
  }
  return useStoreWithEqualityFn(store, selector, (left, right) => left === right)
}

export function Counter(): ReactNode {
  const count = useCounterStore((state) => state.count)
  const increment = useCounterStore((state) => state.increment)

  return (
    <button type="button" onClick={increment}>
      Count: {count}
    </button>
  )
}

export function App(): ReactNode {
  return (
    <>
      <CounterProvider count={2}>
        <Counter />
      </CounterProvider>
      <CounterProvider count={10}>
        <Counter />
      </CounterProvider>
    </>
  )
}
```

The two buttons start at `Count: 2` and `Count: 10`. Clicking one changes only its provider's store. The context wrapper also gives you a single place to reject consumers rendered outside the provider.

![Two mounted counters show Count: 2 and Count: 10.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/424bfa87a15fe5d592cc96db23e9ea42.png)

## Create one store per request

A server can handle concurrent requests, so do not share request-specific state in a module-level store. Export a factory and call it at the request or provider boundary. On the client, initialize the provider's store once with `useState`; on the server and client, use matching initial data to avoid hydration differences.

```ts
import { createStore } from 'zustand/vanilla'

type RequestState = {
  requestId: string
}

export const createRequestStore = (requestId: string) =>
  createStore<RequestState>()(() => ({ requestId }))

export function loadRequestState(requestId: string): string {
  const store = createRequestStore(requestId)
  return store.getState().requestId
}
```

Each call to `createRequestStore` returns a separate instance, so one request's state is not reused by another. React Server Components must not read from or write to the store; keep store access in client components and pass the request's initial data into the client-side provider.

## Choosing the boundary

| Need | Store shape | Binding |
| --- | --- | --- |
| State shared by the application | One module-level store | `create` and its returned hook |
| State used by React and non-React code | One standalone store | `createStore`, then `useStore` |
| Independent instances in different subtrees | Factory-created stores | `createStore`, context, and `useStore` |
| Request-specific or SSR state | One factory call per request | Create the store at the request/provider boundary |

The lower-level store API is useful here because scoping is about which store instance a component receives. Do not add context merely to share a genuinely global store; use the direct hook for that case.

See [Create a vanilla store](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/create-a-vanilla-store) for store construction details and [Handle server-rendered stores](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/handle-server-rendered-stores) for the server-rendering constraints.
