Skip to content

Family

:::caution Deprecated atomFamily is deprecated and will be removed in Jotai v3.

Please migrate to the jotai-family package, which provides the same API with additional features like atomTree.

Migration:

Terminal window
npm install jotai-family
// Before
import { atomFamily } from 'jotai/utils'
// After
import { atomFamily } from 'jotai-family'

The API is identical, so no code changes are needed beyond the import statement. :::

Ref: https://github.com/pmndrs/jotai/issues/23

atomFamily(initializeAtom, areEqual): (param) => Atom

This will create a function that takes param and returns an atom. If the atom has already been created, it will be returned from the cache. initializeAtom is a function that can return any kind of atom (atom(), atomWithDefault(), …). Note that the areEqual argument is optional and compares if two params are equal (defaults to Object.is).

To reproduce behavior similar to Recoil’s atomFamily/selectorFamily, specify a deepEqual function to areEqual. For example:

import { atom } from 'jotai'
import { atomFamily } from 'jotai/utils'
import deepEqual from 'fast-deep-equal'
const fooFamily = atomFamily((param) => atom(param), deepEqual)

The atom family types will be inferred from initializeAtom. Here’s a typical usage with a primitive atom.

import type { PrimitiveAtom } from 'jotai'
/**
* here the atom(id) returns a PrimitiveAtom<number>
* and PrimitiveAtom<number> is a WritableAtom<number, SetStateAction<number>>
*/
const myFamily = atomFamily((id: number) => atom(id))

You can explicitly declare the type of parameter and atom type using TypeScript generics.

atomFamily<Param, AtomType extends Atom<unknown>>(
initializeAtom: (param: Param) => AtomType,
areEqual?: (a: Param, b: Param) => boolean
): AtomFamily<Param, AtomType>

Example with explicit types:

import { atom } from 'jotai'
import type { PrimitiveAtom } from 'jotai'
import { atomFamily } from 'jotai/utils'
const myFamily = atomFamily<number, PrimitiveAtom<number>>((id: number) =>
atom(id),
)

The atomFamily function returns an object with the following methods:

Returns an atom for the given param. If the atom has already been created, it will be returned from the cache.

Returns an iterable of all params currently in the cache.

const todoFamily = atomFamily((name) => atom(name))
todoFamily('foo')
todoFamily('bar')
for (const param of todoFamily.getParams()) {
console.log(param) // 'foo', 'bar'
}

Removes a specific param from the cache.

todoFamily.remove('foo')

Registers a shouldRemove function which runs immediately and when you are about to get an atom from the cache.

  • shouldRemove is a function that takes two arguments: createdAt (in milliseconds) and param, and returns a boolean value.
  • Setting null will remove the previously registered function.
// Remove atoms older than 1 hour
todoFamily.setShouldRemove((createdAt, param) => {
return Date.now() - createdAt > 60 * 60 * 1000
})

⚠️ Unstable API: This API is for advanced use cases and can change without notice.

Fires when an atom is created or removed. Returns a cleanup function.

const cleanup = todoFamily.unstable_listen((event) => {
console.log(event.type) // 'CREATE' or 'REMOVE'
console.log(event.param) // the param
console.log(event.atom) // the atom
})
// Later, stop listening
cleanup()

Internally, atomFamily is just a Map whose key is a param and whose value is an atom config. Unless you explicitly remove unused params, this leads to memory leaks. This is crucial if you use infinite number of params.

Use myFamily.remove(param) or myFamily.setShouldRemove(shouldRemove) to manage memory.

import { atom } from 'jotai'
import { atomFamily } from 'jotai/utils'
const todoFamily = atomFamily((name) => atom(name))
todoFamily('foo')
// this will create a new atom('foo'), or return the one if already created
import { atom } from 'jotai'
import { atomFamily } from 'jotai/utils'
const todoFamily = atomFamily((name) =>
atom(
(get) => get(todosAtom)[name],
(get, set, arg) => {
const prev = get(todosAtom)
set(todosAtom, { ...prev, [name]: { ...prev[name], ...arg } })
},
),
)
import { atom } from 'jotai'
import { atomFamily } from 'jotai/utils'
const todoFamily = atomFamily(
({ id, name }) => atom({ name }),
(a, b) => a.id === b.id,
)

Open in StackBlitz


For new projects or when updating existing code, we recommend using the jotai-family package instead. It provides:

  • Same API: Drop-in replacement for atomFamily
  • Additional features: Includes atomTree for hierarchical atom management
  • Better maintenance: Dedicated package with focused development
  • Future-proof: Will continue to be supported in Jotai v3 and beyond

See the jotai-family documentation for more details.