Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 7 additions & 9 deletions apps/docs/content/06-platform/02-static-site-generation.md
Original file line number Diff line number Diff line change
Expand Up @@ -153,23 +153,21 @@ The first command builds the client bundle. The second builds the SSR entry, and

## Client Hydration

Static pages are hydrated on the client just like SSR pages. The client entry provides the Navigation layer:
Static pages are hydrated on the client just like SSR pages. Pass the client layer via `options.layers` so `hydrate` builds it in its long-lived scope — baking it in via `Effect.provide(App(), layer)` would tear the layer down as soon as the element function returns, killing the popstate listener and reactive subscriptions:

```typescript
// src/client.ts
import { Effect } from "effect";
import { hydrate } from "@effex/dom/hydrate";
import { Navigation } from "@effex/router";
import { Platform } from "@effex/platform";

import { App } from "./app.js";
import { router } from "./routes.js";

const navLayer = Navigation.makeLayer(router);

hydrate(
Effect.provide(App(), navLayer),
document.getElementById("root")!,
);
hydrate(App(), document.getElementById("root")!, {
layers: Platform.makeClientLayer(router),
});
```

`Platform.makeClientLayer` provides both `NavigationContext` and `RouteDataProvider` — the latter reads the SSG-embedded `window.__EFFEX_DATA__` on first load, then fetches `<path>?_data=1` for subsequent navigations (the Vite plugin strips loaders from the client bundle, so this fetch is how loader data reaches the client after hydration).

After hydration, clicking a Link triggers client-side navigation — the browser doesn't reload the page.
10 changes: 7 additions & 3 deletions apps/docs/content/quick-start.md
Original file line number Diff line number Diff line change
Expand Up @@ -295,13 +295,17 @@ export const document = {

### Client hydration

Like SSR, the client hydrates after the static HTML loads. But since there's no server at runtime, `Platform.makeClientLayer` isn't needed — route data is embedded in the HTML:
Like SSR, the client hydrates after the static HTML loads. SSG still needs `Platform.makeClientLayer` — it provides `NavigationContext` (for `Outlet`/`Link`) and reads the SSG-embedded `window.__EFFEX_DATA__` on first load. On subsequent client-side navigations it fetches the matching HTML shell and pulls the embedded data out (the Vite plugin strips loaders from the client bundle, so this is how loader data reaches routes after hydration):

```typescript
import { hydrate } from "@effex/dom";
import { hydrate } from "@effex/dom/hydrate";
import { Platform } from "@effex/platform";
import { App } from "./App.js";
import { router } from "./routes.js";

hydrate(App(), document.getElementById("root")!);
hydrate(App(), document.getElementById("root")!, {
layers: Platform.makeClientLayer(router),
});
```

### Scripts
Expand Down
Loading