Skip to main content
shallow is a utility function that performs shallow equality comparison between two values.

Import

import { shallow } from 'zustand/shallow'

Signature

function shallow<T>(valueA: T, valueB: T): boolean

Parameters

valueA
T
required
The first value to compare
valueB
T
required
The second value to compare

Returns

equal
boolean
Returns true if the values are shallowly equal, false otherwise

Usage

Comparing primitives

import { shallow } from 'zustand/shallow'

shallow('John Doe', 'John Doe')  // true
shallow(10, 10)                   // true
shallow(true, true)               // true
shallow(1n, 1n)                   // true

Comparing objects

const objA = { firstName: 'John', lastName: 'Doe', age: 30 }
const objB = { firstName: 'John', lastName: 'Doe', age: 30 }

Object.is(objA, objB)  // false (different references)
shallow(objA, objB)     // true (same properties and values)

Comparing nested objects (limitation)

const objA = {
  name: 'John',
  address: { city: 'New York', zip: '10001' }
}
const objB = {
  name: 'John',
  address: { city: 'New York', zip: '10001' }
}

shallow(objA, objB)  // false (nested objects have different references)
Shallow comparison only checks top-level properties. Nested objects are compared by reference, not by value.

Comparing arrays

const arrA = [1, 2, 3]
const arrB = [1, 2, 3]

shallow(arrA, arrB)  // true (same length and elements)

Comparing Sets

const setA = new Set([1, 2, 3])
const setB = new Set([1, 2, 3])

shallow(setA, setB)  // true (same elements)

Comparing Maps

const mapA = new Map([['a', 1], ['b', 2]])
const mapB = new Map([['a', 1], ['b', 2]])

shallow(mapA, mapB)  // true (same key-value pairs)

Use with zustand

As an equality function

import { create } from 'zustand'
import { shallow } from 'zustand/shallow'

type State = {
  user: { name: string; age: number }
  settings: { theme: string }
}

const useStore = create<State>()(() => ({
  user: { name: 'John', age: 30 },
  settings: { theme: 'dark' },
}))

function Component() {
  const user = useStore(
    (state) => state.user,
    shallow  // Prevents re-render if user object has same properties
  )
}

With useStoreWithEqualityFn

import { useStoreWithEqualityFn } from 'zustand/traditional'
import { shallow } from 'zustand/shallow'

function Component() {
  const { name, age } = useStoreWithEqualityFn(
    store,
    (state) => ({ name: state.user.name, age: state.user.age }),
    shallow
  )
}

How it works

  1. Reference equality: First checks if valueA and valueB are the same reference using Object.is
  2. Type checking: Returns false if either value is not an object or is null
  3. Prototype check: Returns false if objects have different prototypes
  4. Iterable comparison: For iterables (arrays, Sets, Maps), compares elements/entries
  5. Object comparison: For plain objects, compares all enumerable properties

Prototype checking

const a = Object.create({})  // prototype is {}
const b = {}                  // prototype is Object.prototype

shallow(a, b)  // false (different prototypes)
Objects created with {} or new Object() inherit from Object.prototype, while Object.create(proto) can have a custom prototype.

Notes

  • Shallow comparison is fast and suitable for most cases
  • Only compares top-level properties by value
  • Nested objects/arrays are compared by reference
  • For deep equality, consider using a deep equality library like lodash.isEqual
  • Commonly used with useStoreWithEqualityFn to optimize re-renders

Build docs developers (and LLMs) love