Advanced / Manual Hydration

onHydrationEnd

Runs callback once, when the hydration phase ends: the root pass of hydrate() is over and every streamed <Loading> boundary (and every hold an integration registers, such as server-component content waiting for its data) has resumed or been cancelled. When no hydration is in progress — before hydrate() starts, after the phase has ended, in a client-only render, on the server — it runs in the next microtask.

Intended for test tooling and markers, such as an attribute an end-to-end test waits for. Do not branch what the app renders on it: read isHydrating() where a value must match the server, and switch to the client-only value from onSettled.

  • Register it after calling hydrate(). Registered before hydration starts, the callback runs in the next microtask and does not wait for hydration work started afterward. The same holds when hydrate() parks because the entry ran while the parser was still inside the server shell (an async entry script): hydration starts at the shell-end marker, after hydrate() has returned.
  • It does not say hydration succeeded. When a document root's module preload fails, hydration is abandoned with the server markup left in place and not interactive, and the callback still runs. A non-document root whose preload fails runs it before falling back to a client render.
  • It is not page-wide completion with several hydration roots. A root whose render waits on its module preload is not counted, so another root finishing can end the phase before that root has claimed anything.

Import

import { onHydrationEnd } from "solid-js";

Type signature

function onHydrationEnd(callback: () => void): void;

Parameters

callback

  • Type: () => void

Examples

hydrate(() => <App />, document);
onHydrationEnd(() => {
document.documentElement.dataset.hydrated = "";
});

Learn more

Last updated: 10/15/26, 9:29 AMEdit this pageReport an issue with this page