Non-Destructive Hydration & Event Replay Mechanics
- Last updated
- Prerequisites:
- Angular SSR/SSG basics
- Understanding of the DOM lifecycle and event listeners
- angular
- hydration
- ssr
- event-replay
- server-side-rendering
- dom
Read at your depth
The practical view
Hydration is the process where Angular, after server-side rendering HTML, attaches client-side behavior to that existing HTML instead of re-creating it. With hydration enabled (provideClientHydration()), Angular reuses the SSR-generated DOM nodes and wires up event listeners, avoiding a destructive re-render (which caused flicker and broke focus/scroll). In Angular 18+, event replay is also available: user interactions that happened before hydration completed (clicks on buttons rendered by SSR) are recorded and replayed once the app hydrates, so no click is lost. This gives near-instant first paint plus full interactivity.
The same idea in other frameworks
React 19's hydration + (experimental) event replay are the direct analogues.provideClientHydration() + withEventReplay() — the current topic.Vue's SSR hydration reuses the DOM; event replay is handled by Vue 3.4's hydration timings differently.SvelteKit hydrates and reuses DOM; Svelte 5's event delegation provides similar early-interaction support.Legacy vs modern
Destructive re-render vs non-destructive hydration
Without hydration, Angular re-creates the DOM after SSR (flicker, lost focus); with hydration, the SSR DOM is adopted in place and early events replay.
bootstrapApplication(App); // ❌ OLD: no provideClientHydration → Angular re-creates the DOMbootstrapApplication(App, {
providers: [provideClientHydration(withEventReplay())], // ✅ NEW
});Interview gotchas
Context
This tests the matching algorithm and its failure modes — interviewers want to know why some SSR apps hit hydration mismatches and how the framework reacts.
The mechanical answer
Hydration reuses a node when it matches the expected template structure: same element type, same order, and consistent Angular annotations (component host markers, i18n nodes). Anything that makes the client's expected tree differ from the SSR HTML — dynamic content that renders differently on server vs client (e.g. reading localStorage, window size, Date), structural directives whose data changed between server and client render, or third-party DOM mutation of the SSR markup — causes a mismatch. Angular logs a hydration error (NG0500 series) and falls back to re-rendering that subtree (destructive for that branch only) to avoid a broken UI. The transfer state mechanism also carries serialized data so client render matches server state.
Trap
The naive answer is 'hydration works unless you mutate the DOM'. Mismatches also come from nondeterministic rendering (Date.now(), Math.random, window access) and from services that produce different data on server vs client. Another trap: assuming a mismatch breaks the whole app — Angular falls back per-subtree, but silent mismatches can cause event/listener wiring errors, which is why the errors are worth fixing. Candidates who mention NG0500 errors and the per-subtree fallback show real experience.
Context
This probes the mechanism and its limits — interviewers want the buffering-then-replay design and an honest account of which events are replayable.
The mechanical answer
Event replay installs capture-phase listeners very early (before hydration) for a set of common events (click, input, keydown, etc.). Events dispatched on SSR nodes during the pre-hydration window are recorded — the engine captures the event type, target node path, and payload — and stored in a buffer. When hydration completes, Angular re-dispatches the buffered events on the now-hydrated nodes, so handlers fire as if they had been attached all along. The limitations: only the configured event types are captured, and events depending on exact timing/coordinates or on listener-state that changed during hydration may behave slightly differently. Also, events on nodes that get removed during hydration cannot always be replayed faithfully.
Trap
The naive answer is 'event replay captures every event'. It captures a configured subset (click/input etc.), and complex events (drag sequences, pointer events with coordinates) may replay imperfectly. Another trap: thinking replay makes hydration synchronous — replay happens after hydration completes, so there is still a (short) window where the UI is static. Candidates who can enumerate the captured event types and the replay timing show real depth.