The createWithEqualityFn function creates a React Hook-based store with support for custom equality functions. This allows fine-grained control over when components re-render, improving performance.
Import
import { createWithEqualityFn } from 'zustand/traditional'
import { createWithEqualityFn } from 'zustand/traditional'
You need to install use-sync-external-store as a dependency to use createWithEqualityFn: npm install use-sync-external-store
The zustand/traditional package relies on useSyncExternalStoreWithSelector.
Type Signature
function createWithEqualityFn < T , Mos extends [ StoreMutatorIdentifier , unknown ][] = []>(
initializer : StateCreator < T , [], Mos >,
defaultEqualityFn ?: < U >( a : U , b : U ) => boolean
) : UseBoundStoreWithEqualityFn < Mutate < StoreApi < T >, Mos >>
// Curried version
function createWithEqualityFn < T >() : < Mos extends [ StoreMutatorIdentifier , unknown ][] = []>(
initializer : StateCreator < T , [], Mos >,
defaultEqualityFn ?: < U >( a : U , b : U ) => boolean
) => UseBoundStoreWithEqualityFn < Mutate < StoreApi < T >, Mos >>
Parameters
initializer
StateCreator<T, [], Mos>
required
A function that receives set, get, and store as arguments and returns the initial state object with actions. The function signature is: ( set : SetState < T >, get : GetState < T >, store : StoreApi < T >) => T
defaultEqualityFn
(a: U, b: U) => boolean
default: "Object.is"
An optional equality function used by default when selecting state. Common options:
Object.is (default) - Reference equality
shallow - Shallow equality for objects and arrays
Custom function - Your own comparison logic
Returns
useBoundStore
UseBoundStoreWithEqualityFn<StoreApi<T>>
A React Hook with equality function support: () : T // Returns the entire state
< U >(
selector : ( state : T ) => U ,
equalityFn ?: ( a : U , b : U ) => boolean
) : U // Returns a selected slice with optional per-selector equality
The hook also has API utilities attached: setState, getState, getInitialState, and subscribe.
Basic Usage with Shallow Equality
import { createWithEqualityFn } from 'zustand/traditional'
import { shallow } from 'zustand/shallow'
interface BearState {
bears : number
fish : number
increase : () => void
}
const useBearStore = createWithEqualityFn < BearState >()(
( set ) => ({
bears: 0 ,
fish: 0 ,
increase : () => set (( state ) => ({ bears: state . bears + 1 })),
}),
shallow // Use shallow equality by default
)
function BearAndFish () {
// This component only re-renders when bears OR fish changes
// With shallow equality, object comparison is by value
const { bears , fish } = useBearStore (
( state ) => ({ bears: state . bears , fish: state . fish })
)
return < div > Bears : { bears }, Fish : { fish } </ div >
}
import { createWithEqualityFn } from 'zustand/traditional'
import { shallow } from 'zustand/shallow'
const useBearStore = createWithEqualityFn (
( set ) => ({
bears: 0 ,
fish: 0 ,
increase : () => set (( state ) => ({ bears: state . bears + 1 })),
}),
shallow
)
function BearAndFish () {
const { bears , fish } = useBearStore (
( state ) => ({ bears: state . bears , fish: state . fish })
)
return < div > Bears: { bears } , Fish: { fish } </ div >
}
Per-Selector Equality Function
You can override the default equality function for specific selectors:
import { createWithEqualityFn } from 'zustand/traditional'
import { shallow } from 'zustand/shallow'
const useStore = createWithEqualityFn < State >()(
( set ) => ({ /* ... */ }),
shallow // Default equality
)
function Component () {
// Use custom equality for this specific selector
const data = useStore (
( state ) => state . complexData ,
( a , b ) => a . id === b . id // Custom equality
)
return < div >{data. id } </ div >
}
Custom Equality Function
Create your own equality function for specialized comparisons:
import { createWithEqualityFn } from 'zustand/traditional'
interface User {
id : string
name : string
email : string
}
interface UserState {
users : User []
addUser : ( user : User ) => void
}
// Custom equality: only compare user IDs
const userIdsEqual = ( a : User [], b : User []) => {
if ( a . length !== b . length ) return false
return a . every (( user , i ) => user . id === b [ i ]. id )
}
const useUserStore = createWithEqualityFn < UserState >()(
( set ) => ({
users: [],
addUser : ( user ) => set (( state ) => ({
users: [ ... state . users , user ]
})),
}),
userIdsEqual
)
Shallow Equality Comparison
The shallow function from zustand/shallow performs shallow comparison:
import { createWithEqualityFn } from 'zustand/traditional'
import { shallow } from 'zustand/shallow'
const useStore = createWithEqualityFn < State >()( initializer , shallow )
// These will not cause re-renders if the values haven't changed:
const obj = useStore (( s ) => ({ a: s . a , b: s . b })) // Shallow object comparison
const arr = useStore (( s ) => [ s . x , s . y ]) // Shallow array comparison
Shallow equality compares objects and arrays by their immediate properties/elements. It’s useful when you’re selecting multiple primitive values into an object or array.
Without Equality Function
If you don’t specify an equality function, Object.is is used (reference equality):
import { createWithEqualityFn } from 'zustand/traditional'
const useStore = createWithEqualityFn < State >()(( set ) => ({
count: 0 ,
increment : () => set (( state ) => ({ count: state . count + 1 })),
}))
// This uses Object.is (reference equality)
const count = useStore (( state ) => state . count )
import { createWithEqualityFn } from 'zustand/traditional'
import { shallow } from 'zustand/shallow'
interface TodoState {
todos : Todo []
filter : 'all' | 'active' | 'completed'
addTodo : ( text : string ) => void
setFilter : ( filter : TodoState [ 'filter' ]) => void
}
const useTodoStore = createWithEqualityFn < TodoState >()(
( set ) => ({
todos: [],
filter: 'all' ,
addTodo : ( text ) => set (( state ) => ({
todos: [ ... state . todos , { id: Date . now (), text , completed: false }]
})),
setFilter : ( filter ) => set ({ filter }),
}),
shallow
)
function TodoList () {
// Only re-renders when the filtered list actually changes
// Not when other state properties change
const { todos , filter } = useTodoStore (
( state ) => ({
todos: state . todos . filter ( /* filter logic */ ),
filter: state . filter ,
})
)
return <>{ /* render todos */ } </>
}
When to Use
Selecting Objects When your selectors return new objects/arrays on every render
Performance Critical When you need fine-grained control over re-renders
Complex Comparisons When you need custom logic to determine if state changed
Multiple Values When selecting multiple primitive values into an object
For most use cases, the standard create function with proper selector design is sufficient. Only use createWithEqualityFn when you have specific performance requirements or need custom equality logic.
create - Standard store creation without custom equality
shallow - Shallow equality comparison utility
useStore - Using stores in React components