# Selectors and rendering

Selectors decide which part of a store a component reads; the selected result and its equality
function decide whether that component renders again.

## How the pieces fit

```mermaid
flowchart LR
  Store["Zustand store"] --> Selector["selector(state)"]
  Selector --> Result["selected result"]
  Result --> Equality["Object.is or equality function"]
  Equality -->|different| Render["component renders"]
  Equality -->|equal| Skip["component keeps its render"]
```

The default comparison is `Object.is`. Selecting an atomic value such as a number, string, or
action usually gives a stable result. A selector that constructs an object or array creates a new
reference, even when its contents are unchanged, so use a stable selector output or a shallow
comparison for that case.

## Select atomic values directly

Create the store with [`create`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#create), then pass a selector to the returned hook.
Each selector subscribes the component to one result, so changing another state property does not
change that result.

```tsx
import { create } from 'zustand'

type MealStore = {
  papaBear: string
  count: number
  setMeal: (meal: string) => void
}

const useMealStore = create<MealStore>()((set) => ({
  papaBear: 'large porridge-pot',
  count: 0,
  setMeal: (meal) => set({ papaBear: meal }),
}))

export default function App() {
  const meal = useMealStore((state) => state.papaBear)
  const setMeal = useMealStore((state) => state.setMeal)

  return (
    <main>
      <p>Meal: {meal}</p>
      <button type="button" onClick={() => setMeal('a large pizza')}>
        Order pizza
      </button>
    </main>
  )
}
```

![The mounted counter displays the selected meal and an Order pizza button.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/fcb7f26b7bc4ad62596107075994f82d.png)

The component reads the meal and action separately. Clicking the button changes the displayed meal;
the `count` state is not part of either selected result.

## Select several values with `useShallow`

Wrap a computed selector with [`useShallow`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand-react-shallow#useshallow) when it returns
an object or array whose entries are shallow-equal to the previous result.

```tsx
import { create } from 'zustand'
import { useShallow } from 'zustand/react/shallow'

type FruitStore = {
  apples: number
  oranges: number
  addApple: () => void
}

const useFruitStore = create<FruitStore>()((set) => ({
  apples: 1,
  oranges: 2,
  addApple: () => set((state) => ({ apples: state.apples + 1 })),
}))

export default function App() {
  const fruit = useFruitStore(
    useShallow((state) => ({ apples: state.apples, oranges: state.oranges })),
  )
  const addApple = useFruitStore((state) => state.addApple)

  return (
    <main>
      <p>
        Apples: {fruit.apples}; oranges: {fruit.oranges}
      </p>
      <button type="button" onClick={addApple}>
        Add apple
      </button>
    </main>
  )
}
```

![The mounted fruit component displays apple and orange counts with an Add apple button.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/3440560938e07531713306376e40f618.png)

For a direct comparison, [`shallow`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand-shallow#shallow) takes two values and returns a
boolean. It compares object entries and ordered iterable values using `Object.is` for their entries;
use it when you need to supply an equality function yourself.

## Use a custom equality function

In v5, [`create`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#create) does not accept a custom equality function. Use
[`createWithEqualityFn`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand-traditional#createwithequalityfn) from `zustand/traditional`
when a store needs a default equality function. The `zustand/traditional` entry point requires the
`use-sync-external-store` package.

```tsx
import { createWithEqualityFn } from 'zustand/traditional'
import { shallow } from 'zustand/shallow'

type PositionStore = {
  x: number
  y: number
  move: (x: number, y: number) => void
}

const usePositionStore = createWithEqualityFn<PositionStore>()(
  (set) => ({
    x: 0,
    y: 0,
    move: (x, y) => set({ x, y }),
  }),
  shallow,
)

export default function App() {
  const position = usePositionStore((state) => ({ x: state.x, y: state.y }))
  const move = usePositionStore((state) => state.move)

  return (
    <main>
      <p>
        Position: {position.x}, {position.y}
      </p>
      <button type="button" onClick={() => move(10, 20)}>
        Move
      </button>
    </main>
  )
}
```

![The mounted position component displays coordinates and a Move button.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/4548a2bd8230a08dc322feb961849b13.png)

The position object is compared shallowly, so the component re-renders when `x` or `y` changes and
does not re-render for an update that leaves both selected values equal. Install the peer package
before using this entry point:

```bash
npm install zustand react use-sync-external-store
```

When the store is vanilla and the equality choice belongs to a particular component, use
[`useStoreWithEqualityFn`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand-traditional#usestorewithequalityfn) with the store API, a
selector, and that component's equality function.

```tsx
import { createStore } from 'zustand'
import { shallow } from 'zustand/shallow'
import { useStoreWithEqualityFn } from 'zustand/traditional'

type PositionStore = {
  x: number
  y: number
  move: (x: number, y: number) => void
}

const positionStore = createStore<PositionStore>()((set) => ({
  x: 0,
  y: 0,
  move: (x, y) => set({ x, y }),
}))

export default function App() {
  const position = useStoreWithEqualityFn(
    positionStore,
    (state) => ({ x: state.x, y: state.y }),
    shallow,
  )
  const move = useStoreWithEqualityFn(positionStore, (state) => state.move, shallow)

  return (
    <main>
      <p>
        Position: {position.x}, {position.y}
      </p>
      <button type="button" onClick={() => move(10, 20)}>
        Move
      </button>
    </main>
  )
}
```

![The mounted vanilla-store component displays coordinates and a Move button.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/4548a2bd8230a08dc322feb961849b13.png)

Install React explicitly when using either React entry point; `npm install zustand` does not add this
optional peer dependency.

```bash
npm install zustand react
```

## Bind a vanilla store when the store itself is separate

For a store created outside React, [`createStore`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#createstore) returns a store API.
Pass that API to [`useStore`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#usestore) and give the hook a selector. This keeps the
same selector-and-equality rule while allowing the store instance to be created and supplied by
another part of the application.

```tsx
import { createStore } from 'zustand'
import { useStore } from 'zustand'

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

const counterStore = createStore<CounterStore>()((set) => ({
  count: 0,
  increment: () => set((state) => ({ count: state.count + 1 })),
}))

export default function App() {
  const count = useStore(counterStore, (state) => state.count)
  const increment = useStore(counterStore, (state) => state.increment)

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

The component displays the counter from the separate store and updates when `count` changes.

## Avoid unstable selector outputs

Do not return a fresh object or array from a `create` selector without handling its equality. In v5,
the default `Object.is` comparison sees each fresh reference as different and the selector can cause
an infinite update loop. Choose one of these forms instead:

- Select each value separately.
- Use `createWithEqualityFn` with an equality function when you need that store-wide behavior.

Use a stable fallback value when a selector returns a fallback function; creating a new fallback
function in the selector has the same unstable-reference problem.

## Related

- [How Zustand works](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/how-zustand-works)
- [Optimize computed selections](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/optimize-computed-selections)
- [Create a vanilla store](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/create-a-vanilla-store)
- [Typed store patterns](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/typed-store-patterns)
