# Use custom equality functions

Use this when the default `Object.is` comparison does not match the rule your selector needs. In React, [`createWithEqualityFn`](https://bench-zustand-6l.atloria.app/p/bench-zustand-6l-TzFgfybqJX/developer/zustand-traditional#createwithequalityfn) sets a default comparison for a bound store, while [`useStoreWithEqualityFn`](https://bench-zustand-6l.atloria.app/p/bench-zustand-6l-TzFgfybqJX/developer/zustand-traditional#usestorewithequalityfn) applies a comparison to a store selection.

## Before you start

Install Zustand and the peer dependency required by the [`zustand/traditional`](https://bench-zustand-6l.atloria.app/p/bench-zustand-6l-TzFgfybqJX/developer/zustand-traditional) entry point:

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

If you only need shallow comparison for an object or array selector, prefer [`useShallow`](https://bench-zustand-6l.atloria.app/p/bench-zustand-6l-TzFgfybqJX/developer/zustand-shallow#useshallow). Use a custom equality function when your rule differs from shallow equality.

## Set a store-wide default

Pass a generic equality function as the second argument to `createWithEqualityFn`. The mounted component below displays progress from a store; the default comparator considers numeric values equal while they round to the same integer, so the displayed value changes when progress crosses a half-integer threshold.

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

type ProgressStore = {
  progress: number
  advance: () => void
}

const sameRoundedValue = <T,>(previous: T, next: T): boolean => {
  if (typeof previous === 'number' && typeof next === 'number') {
    return Math.round(previous) === Math.round(next)
  }

  return Object.is(previous, next)
}

const useProgressStore = createWithEqualityFn<ProgressStore>()(
  (set) => ({
    progress: 0,
    advance: () => set((state) => ({ progress: state.progress + 0.2 })),
  }),
  sameRoundedValue,
)

export default function App() {
  const progress = useProgressStore((state) => state.progress)
  const advance = useProgressStore((state) => state.advance)

  return (
    <section>
      <p>Progress: {progress.toFixed(1)}</p>
      <button type="button" onClick={advance}>
        Advance by 0.2
      </button>
    </section>
  )
}
```

![Look at the mounted progress readout and its advance button in the store-wide equality example.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/39b9dc8ec3497f335a6336373d804ce1.png)

The custom function is the store hook's default equality function. Because it also receives selections of other types, it uses `Object.is` for non-numeric values; the `advance` action therefore retains its normal reference comparison.

## Set equality for one selection

For a store API you already have, pass the store, selector, and equality function to `useStoreWithEqualityFn`. This example uses the API attached to a bound store, then subscribes to its progress value with a per-selection comparison. The page shows tenths for this selection, while the store's own hook continues to use its default comparator.

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

type ProgressStore = {
  progress: number
  advance: () => void
}

const useProgressStore = createWithEqualityFn<ProgressStore>()((set) => ({
  progress: 0,
  advance: () => set((state) => ({ progress: state.progress + 0.2 })),
}))

export default function App() {
  const progress = useStoreWithEqualityFn(
    useProgressStore,
    (state) => state.progress,
    (previous, next) => Math.floor(previous * 10) === Math.floor(next * 10),
  )
  const advance = useProgressStore((state) => state.advance)

  return (
    <section>
      <p>Progress: {progress.toFixed(1)}</p>
      <button type="button" onClick={advance}>
        Advance by 0.2
      </button>
    </section>
  )
}
```

![Look at the mounted progress readout and its advance button in the per-selection equality example.](https://atloriaassets.blob.core.windows.net/assets/a438221c-c1af-407c-b9e6-a56c986db846/39b9dc8ec3497f335a6336373d804ce1.png)

Here the component's selected value changes in tenths, so each button click updates the displayed value. The equality function compares the selector results, not the complete store state.

## Equality choices

| Option | Type | Default | What it does |
| --- | --- | --- | --- |
| `defaultEqualityFn` in `createWithEqualityFn` | `<U>(a: U, b: U) => boolean` | `Object.is` | Supplies the default comparison used by the bound store's selectors. |
| `equalityFn` in `useStoreWithEqualityFn` | `(a: U, b: U) => boolean` | No custom function; the underlying selector comparison uses its default | Decides whether two selected values are equal for this hook call. |

Return `true` only when the previous and next selected values are interchangeable for the component. A comparator that reports unequal values as equal prevents the component from seeing those updates. Keep the comparison aligned with what the component renders.

## Pitfalls

- `createWithEqualityFn` and `useStoreWithEqualityFn` come from `zustand/traditional`; add `use-sync-external-store` to the application dependencies.
- The default comparator applies to each selection made through the bound hook. Use the hook's optional per-selection equality argument when one selector needs a different rule.
- Equality does not change the store update. It controls whether the selector's consumer observes the new selection and re-renders.

## Related

- [Select state efficiently](https://bench-zustand-6l.atloria.app/p/bench-zustand-6l-TzFgfybqJX/developer/select-state-efficiently) for selector patterns and shallow comparison.
- [Use a vanilla store in React](https://bench-zustand-6l.atloria.app/p/bench-zustand-6l-TzFgfybqJX/developer/use-a-vanilla-store-in-react) for subscribing to a standalone store in a component.
