Skip to main content
useShallow is a React Hook that optimizes re-renders by memoizing selector functions using shallow comparison.

Import

import { useShallow } from 'zustand/react/shallow'

Signature

function useShallow<S, U>(
  selector: (state: S) => U
): (state: S) => U

Parameters

selector
(state: S) => U
required
A selector function that extracts data from the state

Returns

memoizedSelector
(state: S) => U
A memoized version of the selector that only returns a new reference when the selected values have changed (shallow comparison)

Usage

Optimizing object selections

Without useShallow, selecting multiple values as an object causes re-renders on any state change:
import { create } from 'zustand'

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

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

// This re-renders whenever ANY state changes
function BearNames() {
  const names = useBearStore((state) => Object.keys(state))
  return <div>{names.join(', ')}</div>
}
With useShallow, the component only re-renders when the keys actually change:
import { create } from 'zustand'
import { useShallow } from 'zustand/react/shallow'

function BearNames() {
  const names = useBearStore(
    useShallow((state) => Object.keys(state))
  )
  // Only re-renders when the array of keys changes
  return <div>{names.join(', ')}</div>
}

Selecting multiple properties

function BearDetails() {
  const { papaBear, mamaBear } = useBearStore(
    useShallow((state) => ({
      papaBear: state.papaBear,
      mamaBear: state.mamaBear,
    }))
  )
  // Only re-renders when papaBear or mamaBear change
  
  return (
    <div>
      <p>Papa: {papaBear}</p>
      <p>Mama: {mamaBear}</p>
    </div>
  )
}

Selecting arrays

type TodoStore = {
  todos: Array<{ id: string; text: string; done: boolean }>
  toggleTodo: (id: string) => void
}

const useTodoStore = create<TodoStore>()((set) => ({
  todos: [],
  toggleTodo: (id) =>
    set((state) => ({
      todos: state.todos.map((todo) =>
        todo.id === id ? { ...todo, done: !todo.done } : todo
      ),
    })),
}))

function TodoList() {
  const todoIds = useTodoStore(
    useShallow((state) => state.todos.map((todo) => todo.id))
  )
  // Only re-renders when the array of IDs changes
  
  return todoIds.map((id) => <TodoItem key={id} id={id} />)
}

When to use

Use useShallow when:
  • Selecting multiple properties as an object or array
  • The selector returns a new reference every time (like Object.keys(), map(), etc.)
  • You want to prevent unnecessary re-renders
Don’t use useShallow when:
  • Selecting a single primitive value
  • The selector already returns a stable reference
  • You need deep equality comparison (use a custom equality function instead)

Notes

  • useShallow uses shallow comparison, checking only top-level properties
  • For nested objects, consider using a custom equality function with useStoreWithEqualityFn
  • The hook wraps the selector and maintains a ref to the previous value
  • It’s similar to using useStoreWithEqualityFn with shallow from zustand/shallow

Build docs developers (and LLMs) love