Skip to content

Hydrate client state

The server container never crosses the React Server Component boundary. Transfer only a serializable snapshot and restore it into a separate client-safe container.

server state -> serializable snapshot -> client state
  1. Make the state hydratable

    Implement Hydratable<TSnapshot> with a serializable snapshot type.

  2. Define a typed hydration schema

    const hydration = {
    user: USER_STATE,
    } satisfies HydrationSchema;
  3. Create the server snapshot

    const snapshot = dehydrate({
    container: getRequestContainer(),
    schema: hydration,
    });
  4. Restore it on the client

    hydrate({
    container: clientContainer,
    schema: hydration,
    snapshot,
    });
examples/next/hydration.ts
import { createContainer, createToken, provideFactory } from "di-craft";
import { hydrate } from "di-craft/next/client";
import {
dehydrate,
type Hydratable,
type HydrationSchema,
} from "di-craft/next/server";
type UserSnapshot = {
readonly users: readonly string[];
};
class UserState implements Hydratable<UserSnapshot> {
private users: readonly string[] = [];
setUsers(users: readonly string[]): void {
this.users = users;
}
dehydrate(): UserSnapshot {
return { users: this.users };
}
hydrate(snapshot: UserSnapshot): void {
this.users = snapshot.users;
}
}
const USER_STATE = createToken<UserState>("USER_STATE");
const hydration = {
user: USER_STATE,
} satisfies HydrationSchema;
const serverContainer = createContainer([
provideFactory(USER_STATE, {
useFactory: () => new UserState(),
}),
]);
serverContainer.get(USER_STATE).setUsers(["Ada", "Grace"]);
const snapshot = dehydrate({
container: serverContainer,
schema: hydration,
});
// The client owns a separate client-safe container. Only the serializable
// snapshot crosses the server/client boundary.
const clientContainer = createContainer([
provideFactory(USER_STATE, {
useFactory: () => new UserState(),
}),
]);
hydrate({
container: clientContainer,
schema: hydration,
snapshot,
});

For streamed UI, let Suspense, loading.js, and React’s use model control the rendering boundary. di-craft only creates the data or snapshot; it is not a client state manager.