This repository has been archived on 2026-05-05. You can view files and clone it. You cannot open issues or pull requests or push a commit.
Files
Will Anderson 143137e2b4 add ssr-counter example demonstrating SSR + hydration end-to-end
Counter.el defines Counter, CounterPair, and App components with {#if}
conditionals, event handlers, and child component composition.

render.js (generated by elc-ui --target=server) is the Node.js render
script the server calls. app.js (generated by elc-ui --target=web) is the
compiled browser module. hydrate.js attaches the runtime to the
server-rendered DOM without replacing it.

index.html shows the integration point with instructions for injecting
server output and the two CLI commands needed to regenerate both targets.
2026-05-04 12:49:19 -05:00

50 lines
2.1 KiB
JavaScript

/**
* hydrate.js — Client-side hydration entry point for ssr-counter.
*
* This script runs in the browser after the server has delivered the
* SSR-rendered HTML. It:
* 1. Finds each server-rendered component root (data-el-component attribute).
* 2. Reads back the initial state snapshot (data-el-state attribute).
* 3. Attaches the el-ui runtime WITHOUT replacing the existing DOM.
* 4. Binds all event handlers so the page becomes interactive.
*
* The page is immediately visible (the SSR HTML), and becomes interactive
* as soon as this script finishes executing. There is no content flash.
*
* SSR + hydration lifecycle:
* Server: renderToString(src, 'App', props) → HTML with markers
* Network: HTML delivered to browser, painted immediately
* Client: hydrate('[data-el-component="App"]', App) → events bound
* spreading activation activates on first setState() call
*/
import { hydrate } from '../../dist/el-ui.js';
import { Counter, CounterPair, App } from './app.js';
// Hydrate each component by its data-el-component selector.
// Order matters: hydrate leaf components before their parents so
// each instance gets its own renderer and state graph.
// Hydrate all Counter instances
document.querySelectorAll('[data-el-component="Counter"]').forEach(el => {
// Read the stable ID to avoid double-hydration
if (el.dataset.elHydrated) return;
hydrate(`[data-el-id="${el.dataset.elId}"]`, Counter);
el.dataset.elHydrated = 'true';
});
// Hydrate all CounterPair instances
document.querySelectorAll('[data-el-component="CounterPair"]').forEach(el => {
if (el.dataset.elHydrated) return;
hydrate(`[data-el-id="${el.dataset.elId}"]`, CounterPair);
el.dataset.elHydrated = 'true';
});
// The App component contains CounterPair and Counter children.
// If you want the App to own the full activation graph, hydrate it here.
// For this example we hydrate individual Counter components so each
// maintains its own independent state graph (the simpler model).
// Uncomment to hydrate the whole App as one unit instead:
// hydrate('[data-el-component="App"]', App);