Capítulo 80 de 80

Chapter 80: Hydration API — dehydrate, hydrate, HydrationBoundary

Core Idea

dehydrate() freezes a QueryClient's cache into a serializable snapshot; hydrate()/HydrationBoundary load that snapshot back into a (possibly different) client — the low-level API behind everything the Server Rendering & Hydration chapters build on.

Key Concepts

  • dehydrate(client, options): returns a DehydratedState — by default includes only successful queries and only paused mutations. Not part of the stable public shape (don't rely on its internal structure) and not itself serialized — apply JSON.stringify or a safer serializer yourself if the destination needs a string.
    • shouldDehydrateQuery(query) / shouldDehydrateMutation(mutation): override which queries/mutations are included — call defaultShouldDehydrateQuery/defaultShouldDehydrateMutation inside a custom implementation to extend rather than replace the default filter (e.g. to also include errored queries).
    • serializeData(data): transform data during dehydration (paired with hydrate's deserializeData on the other side) — for non-JSON-safe values.
    • shouldRedactErrors(error): defaults to redacting all errors from the dehydrated payload — return false per-error to include specific ones (they still need to be independently serializable, e.g. Error objects need manual handling since they aren't JSON-safe by default).
  • hydrate(client, dehydratedState, options): the low-level restore — defaultOptions (both queries and mutations) and deserializeData configurable. Only overwrites an existing cache entry if the incoming data is newer — never regresses fresher client data with older hydrated data.
  • HydrationBoundary: the React-idiomatic wrapper around hydrate — takes state (a dehydrated state) and merges it into the QueryClient from context, again respecting the "only newer data wins" rule. Only hydrates queries, not mutations — use the lower-level hydrate function (or the persistQueryClient plugin) if mutations need to survive dehydration too.

Code Examples

// Server: include errors too, then serialize safely (Error isn't JSON-safe by default)
const state = dehydrate(client, { shouldDehydrateQuery: () => true })
const serializedState = mySerializer(state) // must escape output — plain JSON.stringify is an XSS risk if embedded in HTML

// Client
const state = myDeserializer(serializedState)
hydrate(client, state)
  • What it demonstrates: opting into dehydrating errored queries too, and the serialize/deserialize round trip needed for non-JSON-safe values like Error.

Key Takeaways

  1. HydrationBoundary only carries queries — reach for hydrate/persistQueryClient specifically when mutations must also survive the round trip.
  2. Both hydrate and HydrationBoundary refuse to overwrite fresher existing cache data with older hydrated data — this is a safety guarantee, not a bug if hydration "doesn't seem to update" something already current.
  3. Never JSON.stringify a dehydrated state directly into HTML in a custom SSR setup — use an escaping-safe serializer (see Server Rendering & Hydration) to avoid XSS.

Connects To

  • Server Rendering & Hydration / Advanced Server Rendering: the full context this API is used within.
  • persistQueryClient: uses this same dehydrate/hydrate pair for storage persistence, including mutations.