# Optimize computed selections

Use a selector for the value your component needs, and wrap a selector that creates a new object or array with [`useShallow`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand-react-shallow#useshallow) when its top-level values can stay the same.

## When to use it

Use atomic selectors when you need individual values. Use `useShallow` when one component needs several values or a computed collection such as `Object.keys(state)`. Zustand compares selector results with `Object.is` by default, so a newly created object or array is different even when its contents are unchanged.

Install Zustand and React before using either React entry point:

```bash
npm install zustand react
```

## Select individual values

Create the store with [`create`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand#create), then select each primitive or action separately:

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

type SearchStore = {
  searchValue: string
  setSearchValue: (value: string) => void
}

const useSearchStore = create<SearchStore>((set) => ({
  searchValue: '',
  setSearchValue: (value) => set({ searchValue: value }),
}))

export function SearchBox() {
  const searchValue = useSearchStore((state) => state.searchValue)
  const setSearchValue = useSearchStore((state) => state.setSearchValue)

  return (
    <label>
      Search
      <input
        value={searchValue}
        onChange={(event) => setSearchValue(event.target.value)}
      />
    </label>
  )
}
```

![The mounted search field is visible.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/98553dc230d2ecb83f6d8304398cc448.png)

The component receives the current string and the action without constructing a wrapper object. It re-renders when either selected value changes.

## Keep a computed selection stable

When the selector constructs an object or array, pass it through `useShallow`:

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

type MealStore = {
  papaBear: string
  mamaBear: string
  babyBear: string
}

const useMealStore = create<MealStore>(() => ({
  papaBear: 'large porridge-pot',
  mamaBear: 'middle-size porridge pot',
  babyBear: 'A little, small, wee pot',
}))

export function BearNames() {
  const names = useMealStore(
    useShallow((state) => Object.keys(state)),
  )

  return <div style={{ height: '400px' }}>{names.join(', ')}</div>
}
```

![The mounted component displays the three bear names.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/98553dc230d2ecb83f6d8304398cc448.png)

`useShallow` returns a memoized selector. The component displays `papaBear, mamaBear, babyBear`, and changing a meal value does not change that array's top-level contents, so it does not cause an unnecessary render of `BearNames`. Adding or removing a store key changes the displayed names and causes the component to update.

The same pattern applies to several selected values. This complete component returns one stable object:

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

type SearchStore = {
  searchValue: string
  setSearchValue: (value: string) => void
}

const useSearchStore = create<SearchStore>((set) => ({
  searchValue: 'porridge',
  setSearchValue: (value) => set({ searchValue: value }),
}))

export function SearchSummary() {
  const selection = useSearchStore(
    useShallow((state) => ({
      searchValue: state.searchValue,
      setSearchValue: state.setSearchValue,
    })),
  )

  return (
    <div style={{ height: '400px' }}>
      <output>{selection.searchValue}</output>
    </div>
  )
}
```

The returned object keeps its previous reference when its selected properties are shallow-equal. If you use an array, object, `Map`, or `Set`, the comparison is shallow: nested values are compared by reference rather than recursively.

## Compare values explicitly

Use [`shallow`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand-shallow#shallow) when you need a boolean comparison outside a store subscription:

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

const previous = { firstName: 'John', lastName: 'Doe' }
const next = { firstName: 'John', lastName: 'Doe' }

const unchanged = shallow(previous, next)

export function ComparisonResult() {
  return <output>{String(unchanged)}</output>
}
```

![The mounted comparison result displays true.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/82e56ad551f99be6cd32f1992c660d15.png)

`unchanged` is `true` because the top-level properties match. A nested object with a different reference makes the comparison `false` even when its nested contents look identical.

## Use a custom equality function when shallow comparison is not enough

For a store that needs a custom equality function, use [`createWithEqualityFn`](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/zustand-traditional#createwithequalityfn) from `zustand/traditional`:

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

type ResultStore = {
  result: { value: number; label: string }
}

const useResultStore = createWithEqualityFn<ResultStore>(() => ({
  result: { value: 1, label: 'one' },
}))

const sameResult = (left: ResultStore['result'], right: ResultStore['result']) =>
  left.value === right.value && left.label === right.label

export function Result() {
  const result = useResultStore((state) => state.result, sameResult)
  return <output>{result.label}: {result.value}</output>
}
```

The component uses `sameResult` for this selection, so it updates only when that comparison returns `false`. The `zustand/traditional` entry point requires the `use-sync-external-store` peer dependency; install it with the package:

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

## Avoid unstable selector loops

In v5, this selector creates a new array on every render:

```tsx
type SearchStore = {
  searchValue: string
  setSearchValue: (value: string) => void
}

const unstableSelector = (state: SearchStore) => [
  state.searchValue,
  state.setSearchValue,
]
```

Because the default comparison is `Object.is`, the new reference can trigger a maximum-update-depth loop. Replace it with the `useShallow` version above, or use two atomic selectors. Do not return a fallback function created inside a selector; define the fallback once so the selector returns a stable reference.

## Related

- [Selectors and rendering](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/selectors-and-rendering)
- [How Zustand works](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/how-zustand-works)
- [Create a vanilla store](https://bench-zustand-56.atloria.app/p/bench-zustand-56-D8Xr4XerWj/developer/create-a-vanilla-store)
