---
title: "Hydration"
description: "How Radiant and JSX hydration work, including the explicit client hydrator contract for SSR pages."
group: SSR
order: 2
---
# Hydration
Hydration is the client attach step for HTML that was already rendered on the server.
The important distinction is:
- SSR produces HTML.
- hydration attaches behavior to that existing HTML.
- a normal client render replaces that HTML with a fresh DOM tree.
Radiant makes that boundary explicit for render-owning `RadiantElement` hosts.
## Two Hydration Models
There are two related but different hydration flows in this ecosystem.
### Plain JSX hydration
Use the `@ecopages/jsx` client APIs when you are hydrating a normal container element.
```tsx
/** @jsxImportSource @ecopages/jsx */
import { createRoot } from '@ecopages/jsx';
import { renderToString } from '@ecopages/jsx/server';
function App() {
return ;
}
const html = renderToString(, { mode: 'hydrate' });
const container = document.querySelector('#app');
if (container instanceof HTMLElement) {
createRoot(container).hydrate();
}
```
That flow only cares about JSX hydration markers inside a normal target element.
### Radiant host hydration
Use the explicit Radiant hydrator when the server rendered a custom-element host such as `...` through a render-owning `RadiantElement`.
That flow has one extra rule: the component hydrates in place only when both of these are true on first connect:
1. the host already contains hydration markers
2. the explicit Radiant hydrator has been installed on the client
If either condition is missing, the host falls back to a fresh client render.
## The Radiant Contract
For a hydratable render-owning `RadiantElement` page today, you need three pieces.
### 1. Server-side Radiant SSR runtime
The server render path must import an explicit server entrypoint so the shared SSR runtime is registered.
For adapters, import `@ecopages/radiant/server/install-ssr-runtime` once at server boot (or import `@ecopages/radiant/server/render-component`, which installs it as a side effect). When bundler order is uncertain, prefer the explicit `install-ssr-runtime` import first:
```ts
import '@ecopages/radiant/server/install-ssr-runtime';
```
In application code, the higher-level server helpers under `@ecopages/radiant/server/render-component` are the preferred API.
### 2. Hydrated server markup
The server render must emit hydration markers.
For JSX this means:
```ts
renderToString(view, { mode: 'hydrate' });
```
For render-owning `RadiantElement` hosts this usually means one of:
- `renderComponent(...)` / `renderComponentToString(...)` from `@ecopages/radiant/server/render-component`
- `renderRadiantElementHostToString(...)` from `@ecopages/radiant/server/radiant-element-ssr` when you need the host string directly
- `element.renderViewToString({ mode: 'hydrate' })` when you only need the view fragment (after a server SSR entry is imported)
### 3. Explicit client hydrator install
Install the client hydrator before component modules load:
```ts
import '@ecopages/radiant/client/install-hydrator';
```
Or, if you want explicit bootstrap control:
```ts
import { installRadiantHydrator } from '@ecopages/radiant/client/hydrator';
installRadiantHydrator();
```
This sets the global hydration gate that Radiant checks on first connect for render-owning hosts.
## What Happens On First Connect
When the browser upgrades a server-rendered render-owning `RadiantElement` host, the runtime does this:
1. wait one microtask after `connectedCallback()`
2. inspect the host for hydration markers
3. check whether the explicit hydrator is installed
4. if both are true, call `hydrate()` and reconnect listeners and bindings in place
5. otherwise call `update()` and mount a fresh client render
That last fallback is intentional. It means a page can still work without the hydrator import, but it will stop preserving the exact SSR DOM tree.
## When You Need The Hydrator
Install the explicit hydrator when:
- the server emitted render-owning `RadiantElement` markup with `mode: 'hydrate'`
- you want to preserve the SSR DOM in place on first connect
- you want server-emitted event and property bindings to reconnect without replacing nodes
You do not need it for:
- client-only pages
- plain `@ecopages/jsx` containers that hydrate through `createRoot(...).hydrate(...)`
- pages where a fresh client rerender is acceptable
## What If You Omit It
If you omit the Radiant hydrator import:
- the page can still become interactive
- render-owning hosts fall back to a fresh client render
- SSR nodes may be replaced instead of being hydrated in place
So this is not usually a catastrophic failure. It is a hydration-semantics regression, not necessarily a visible blank-page failure.
## Context And Signal Hydration
Hydration scripts for providers and signals still depend on server-rendered markup being present under the host.
That means the safest path for SSR pages is:
- use the Radiant server helpers to emit host markup
- install the explicit client hydrator before component modules load
This keeps host HTML, hydration markers, context payloads, and signal payloads on one coherent path.
## Recommended Mental Model
Use this rule of thumb:
- `@ecopages/jsx` owns container-level hydration.
- `@ecopages/radiant` owns host-level hydration for render-owning `RadiantElement` custom elements.
- Radiant host hydration is explicit on the client.
## Related Guides
- [RadiantElement](/docs/components/radiant-element) for the component-host lifecycle
- [JSX Client Rendering](/docs/jsx/rendering) for container-level client rendering and hydration
- [JSX SSR](/docs/ssr/jsx-ssr) for server-side HTML generation and marker emission
- [Provide Context](/docs/context/provide-context) for provider hydration payloads inside SSR hosts