useHydrateAtoms is a hook that initializes atom values, primarily used for server-side rendering (SSR) and hydration scenarios. It ensures atoms have specific values when components mount.
When to use it
Use useHydrateAtoms when you need to:
- Initialize atoms with server-side data during SSR
- Hydrate atoms with initial values from props
- Set up initial state for isolated component trees
- Populate atoms with data from external sources on mount
Signature
function useHydrateAtoms(
values: Iterable<readonly [AnyWritableAtom, ...unknown[]]>,
options?: Options
): void
Parameters
values: An iterable of [atom, value] tuples or [atom, ...args] for atoms with write arguments
- Can be an array:
[[atom1, value1], [atom2, value2]]
- Can be a Map:
new Map([[atom1, value1], [atom2, value2]])
options: Optional configuration object
store: Custom store to use (defaults to the store from Provider)
dangerouslyForceHydrate: Force re-hydration even if already hydrated (use with caution)
Returns
Nothing (void). This hook only has side effects.
Basic Usage
import { atom } from 'jotai'
import { useHydrateAtoms } from 'jotai/react/utils'
const countAtom = atom(0)
const nameAtom = atom('Guest')
function Component({ initialCount, initialName }) {
useHydrateAtoms([
[countAtom, initialCount],
[nameAtom, initialName]
])
const [count] = useAtom(countAtom)
const [name] = useAtom(nameAtom)
return (
<div>
<p>Count: {count}</p>
<p>Name: {name}</p>
</div>
)
}
SSR Example
import { atom } from 'jotai'
import { useHydrateAtoms } from 'jotai/react/utils'
const userAtom = atom<User | null>(null)
const postsAtom = atom<Post[]>([])
function Page({ user, posts }) {
// Hydrate atoms with server-rendered data
useHydrateAtoms([
[userAtom, user],
[postsAtom, posts]
])
return (
<div>
<UserProfile />
<PostList />
</div>
)
}
// Server-side
export async function getServerSideProps() {
const user = await fetchUser()
const posts = await fetchPosts()
return {
props: { user, posts }
}
}
With Next.js App Router
import { atom } from 'jotai'
import { useHydrateAtoms } from 'jotai/react/utils'
const dataAtom = atom<Data | null>(null)
function HydrateAtoms({ initialData, children }) {
useHydrateAtoms([[dataAtom, initialData]])
return children
}
export default async function Page() {
const data = await fetchData()
return (
<HydrateAtoms initialData={data}>
<Content />
</HydrateAtoms>
)
}
Using Map for Values
import { atom } from 'jotai'
import { useHydrateAtoms } from 'jotai/react/utils'
const atom1 = atom(0)
const atom2 = atom('')
function Component({ values }) {
const atomValues = new Map([
[atom1, values.count],
[atom2, values.name]
])
useHydrateAtoms(atomValues)
// Component logic
}
Hydration Only Happens Once
By default, atoms are only hydrated once per store:
function Component({ value }) {
// First render: atom is set to value
// Re-renders with different value: atom keeps its current value
useHydrateAtoms([[myAtom, value]])
}
This prevents props from overwriting user interactions.
Force Re-hydration
Use dangerouslyForceHydrate to re-hydrate on every render:
function Component({ value }) {
// Atom will be updated on every render
// WARNING: This can cause issues with user state
useHydrateAtoms(
[[myAtom, value]],
{ dangerouslyForceHydrate: true }
)
}
dangerouslyForceHydrate can overwrite user changes. Only use it when you’re certain atoms should always reflect props.
Isolated Component Trees
import { createStore } from 'jotai'
import { useHydrateAtoms } from 'jotai/react/utils'
import { Provider } from 'jotai/react'
const configAtom = atom<Config | null>(null)
function IsolatedApp({ config }) {
const store = createStore()
return (
<Provider store={store}>
<Hydrate config={config}>
<App />
</Hydrate>
</Provider>
)
}
function Hydrate({ config, children }) {
useHydrateAtoms([[configAtom, config]])
return children
}
With atomWithStorage
import { atomWithStorage } from 'jotai/utils'
import { useHydrateAtoms } from 'jotai/react/utils'
const themeAtom = atomWithStorage('theme', 'light')
function App({ serverTheme }) {
// Hydrate with server-detected theme
useHydrateAtoms([[themeAtom, serverTheme]])
const [theme, setTheme] = useAtom(themeAtom)
return <div className={theme}>{/* app content */}</div>
}
Multiple Initialization Values
import { atom } from 'jotai'
import { useHydrateAtoms } from 'jotai/react/utils'
const userAtom = atom<User | null>(null)
const settingsAtom = atom<Settings>(defaultSettings)
const itemsAtom = atom<Item[]>([])
function Dashboard({ initialData }) {
useHydrateAtoms([
[userAtom, initialData.user],
[settingsAtom, initialData.settings],
[itemsAtom, initialData.items]
])
return (
<div>
<UserProfile />
<Settings />
<ItemList />
</div>
)
}
Pattern: Hydration Component
Create a reusable hydration wrapper:
import { useHydrateAtoms } from 'jotai/react/utils'
function HydrateAtoms({ initialValues, children }) {
useHydrateAtoms(initialValues)
return children
}
function Page({ serverData }) {
return (
<HydrateAtoms initialValues={[
[userAtom, serverData.user],
[postsAtom, serverData.posts]
]}>
<PageContent />
</HydrateAtoms>
)
}
TypeScript
useHydrateAtoms is fully typed and enforces correct value types:
import { atom } from 'jotai'
import { useHydrateAtoms } from 'jotai/react/utils'
const numberAtom = atom(0)
const stringAtom = atom('')
function Component() {
useHydrateAtoms([
[numberAtom, 42], // OK
[stringAtom, 'hello'], // OK
[numberAtom, 'invalid'], // Error: string not assignable to number
])
}
Common Patterns
Pattern 1: SSR with Suspense
import { Suspense } from 'react'
function Page({ initialData }) {
return (
<HydrateAtoms initialValues={[[dataAtom, initialData]]}>
<Suspense fallback={<Loading />}>
<AsyncComponent />
</Suspense>
</HydrateAtoms>
)
}
Pattern 2: Conditional Hydration
function Component({ data }) {
const values = data ? [[myAtom, data]] : []
useHydrateAtoms(values)
}
Pattern 3: Dynamic Atom Hydration
function Component({ items }) {
const atomValues = items.map(item => [
getAtomForItem(item.id),
item.value
])
useHydrateAtoms(atomValues)
}