# Store scope and lifecycle

Store scope is the boundary you choose for sharing a store instance; its lifetime depends on where your application keeps that instance.

The store exists independently of React, and hooks connect React components to it. Creating a store, subscribing a component, and hydrating persisted state are separate operations: mounting a consumer does not create a new instance of an existing store.

## Ownership determines sharing

[`create`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand#create) creates a store and returns a hook bound to it. Every component calling that same hook reads the same store. A module-level hook therefore gives the components importing it shared state without a provider.

[`createStore`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand#createstore) returns a vanilla store instead of a bound hook. Call it inside a factory when you need independent instances. For dependency injection or initialization from component props, keep a vanilla store in React context. [`useStore`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand#usestore) connects a consumer to the instance supplied by that context and returns its selected state.

```mermaid
flowchart TD
  M["Module-level create()"] --> G["Shared store and bound hook"]
  G --> A["Consumer A"]
  G --> B["Consumer B"]
  P["Provider with lazy useState initializer"] --> V["Vanilla store instance"]
  V --> C["Context supplies the instance"]
  C --> D["useStore(instance, selector)"]
  D --> E["Scoped consumer"]
  S["Persisted storage"] -->|"Hydration merges saved state"| V
```

Choose the owner according to the sharing you want:

- **Module:** components using the same exported hook or vanilla store share one instance. Unmounting a consumer does not remove the module's reference to that store.
- **Provider:** descendants share the provider's instance. Separate providers can own separate instances, even when they use the same factory.
- **Keyed collection:** a map can retain one instance per tab or other key. Switching consumers does not remove the entries; the collection owns those references.

The authors' dynamic-store example uses a map to reuse each tab's store. A component subscription is not an ownership mechanism: removing the subscription does not reset the state.

## A module-level store shares updates

In a browser React project with a `root` element, use this as `src/main.tsx`. Both buttons start at zero. Clicking either button increments the count displayed by both, because both consumers use the same bound hook.

```tsx title="src/main.tsx"
import { createRoot } from 'react-dom/client'
import { create } from 'zustand'

type CounterState = {
  count: number
  increment: () => void
}

const useCounterStore = create<CounterState>()((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

function Counter({ label }: { label: string }) {
  const count = useCounterStore((state) => state.count)
  const increment = useCounterStore((state) => state.increment)
  return (
    <button type="button" onClick={increment}>
      {label}: {count}
    </button>
  )
}

function App() {
  return (
    <main>
      <Counter label="First consumer" />
      <Counter label="Second consumer" />
    </main>
  )
}

createRoot(document.getElementById('root')!).render(<App />)
```

This follows the starter's module-level store and separate selectors for state and actions. The [live Zustand demo](https://zustand-demo.pmnd.rs/) also uses a module-level bound hook for its counter.

## A provider initializes an independent instance

Use this alternative `src/main.tsx` to give each provider its own counter. The first provider starts at two and has two consumers; the second starts at ten and has one. Clicking either button in the first group updates both of that group's buttons, but leaves the second group unchanged.

The lazy `useState` initializer keeps the instance stable across provider re-renders. `initialCount` supplies creation-time data, not an ongoing binding: changing that prop on the same mounted provider does not recreate or update its store. A new provider mount creates a new instance from its initial props.

```tsx title="src/main.tsx"
import { createContext, useContext, useState, type ReactNode } from 'react'
import { createRoot } from 'react-dom/client'
import { createStore, useStore } from 'zustand'

type CounterState = {
  count: number
  increment: () => void
}

function createCounterStore(initialCount: number) {
  return createStore<CounterState>()((set) => ({
    count: initialCount,
    increment: () => set((state) => ({ count: state.count + 1 })),
  }))
}

type CounterStore = ReturnType<typeof createCounterStore>
const CounterContext = createContext<CounterStore | null>(null)

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

function useCounter<T>(selector: (state: CounterState) => T): T {
  const store = useContext(CounterContext)
  if (!store) throw new Error('Missing CounterProvider')
  return useStore(store, selector)
}

function Counter({ label }: { label: string }) {
  const count = useCounter((state) => state.count)
  const increment = useCounter((state) => state.increment)
  return (
    <button type="button" onClick={increment}>
      {label}: {count}
    </button>
  )
}

function App() {
  return (
    <main>
      <CounterProvider initialCount={2}>
        <section>
          <h2>First scope</h2>
          <Counter label="Consumer A" />
          <Counter label="Consumer B" />
        </section>
      </CounterProvider>
      <CounterProvider initialCount={10}>
        <section>
          <h2>Second scope</h2>
          <Counter label="Consumer C" />
        </section>
      </CounterProvider>
    </main>
  )
}

createRoot(document.getElementById('root')!).render(<App />)
```

Context carries the vanilla instance, not a bound hook. For a reusable provider pattern, continue with [Initialize scoped stores](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/initialize-scoped-stores).

## Initialization and persistence hydration are different

The state creator runs when you create the store and returns its initial state and actions. A later component subscription reads that existing store; it does not run the state creator again. For construction-time constraints, see [Reset store state](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/reset-store-state).

[`persist`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand-middleware#persist) adds another stage: **hydration retrieves persisted state from storage and merges it with current state**. It does not change which components share the instance.

- With synchronous storage such as `localStorage`, normal automatic hydration completes during store creation.
- With asynchronous storage, hydration completes later, in a microtask. The initial React render can precede that completion. See [Persist store data](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/persist-store-data) for handling that timing.
- With `skipHydration: true`, automatic hydration at initialization is disabled. Call the instance's `persist.rehydrate()` when your application is ready; it returns `void` or a promise. See [Control persistence hydration](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/control-persistence-hydration).

In-memory ownership and storage identity are separate. The persistence `name` identifies the storage entry and must be unique. Separate scoped instances using the same name read and write that same entry, even though their in-memory state is independent.

## Subscriptions connect React without owning the store

The state and listeners behind [`StoreApi`](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/zustand#storeapi) belong to the store instance, not its consumers; see [State and actions](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/state-and-actions) for the individual methods.

`useStore` uses React's `useSyncExternalStore` with the instance's subscription and snapshot methods. React manages that component's subscription lifecycle. Unmounting a consumer ends its subscription; it does not reset or delete the store. Select only the state each consumer needs, as in the samples above. See [Selectors and subscriptions](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/selectors-and-subscriptions) for selector-output equality and rendering behavior.

When you register a listener yourself with `subscribe()`, retain its unsubscribe function and call it when that listener's owner tears down. That removes the listener, not the state. See [Subscribe outside React](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/subscribe-outside-react) for an effect with cleanup.

React's server snapshot uses `getInitialState()`. With persistence, that method returns the state creator's result, while hydration can change current state. Persistence hydration is not React's server/client hydration. For request ownership and matching server/client initialization, follow [Next.js store setup](https://bench-zustand-61.atloria.app/p/bench-zustand-61-pSJCWrWW5Y/developer/nextjs-store-setup).
