[{"data":1,"prerenderedAt":940},["ShallowReactive",2],{"content:\u002Fframework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay":3,"surroundings:\u002Fframework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay":932},{"id":4,"title":5,"body":6,"description":912,"extension":913,"meta":914,"navigation":925,"path":926,"seo":927,"stem":930,"__hash__":931},"content\u002Fframework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay\u002Findex.md","Angular SSR Hydration and Event Replay",{"type":7,"value":8,"toc":893},"minimark",[9,13,28,49,195,200,236,240,246,252,258,264,268,273,297,391,395,417,421,432,436,442,522,526,529,533,539,543,559,670,674,677,681,684,688,714,718,724,730,736,742,746,758,767,779,788,797,806,815,824,833,842,851,855,878,883,886,889],[10,11,5],"h1",{"id":12},"angular-ssr-hydration-and-event-replay",[14,15,16,17,22,23,27],"p",{},"This guide is part of ",[18,19,21],"a",{"href":20},"\u002Fframework-performance\u002Fangular-performance\u002F","Angular Performance",", within ",[18,24,26],{"href":25},"\u002Fframework-performance\u002F","Framework Performance",". A client-rendered Angular app shows nothing meaningful until its JavaScript has downloaded, parsed and bootstrapped, and then fetched data — a long wait on mobile, and poor LCP. Server-side rendering sends complete HTML so content appears immediately. Older Angular SSR used destructive hydration: on bootstrap, the client discarded the server DOM and re-rendered it, causing flicker, layout shifts and wasted work.",[14,29,30,31,35,36,39,40,43,44,48],{},"Modern Angular uses ",[32,33,34],"strong",{},"non-destructive hydration",": the client reuses the server-rendered DOM, attaching listeners and state without recreating nodes. ",[32,37,38],{},"Event replay"," captures user events (clicks, inputs) that happen before hydration completes and replays them once the app is ready, so early interactions are not lost. ",[32,41,42],{},"Incremental hydration"," extends this with ",[45,46,47],"code",{},"@defer"," blocks that hydrate on triggers. Together, they give fast LCP from SSR without the classic hydration problems.",[14,50,51],{},[52,53,59,60,59,67,59,71,59,74,59,92,59,100,59,106,59,113,59,120,59,123,59,127,59,130,59,133,59,136,59,140,59,149,59,152,59,155,59,158,59,164,59,170,59,174,59,178,59,181,59,185,59,190,59],"svg",{"viewBox":54,"width":55,"role":56,"ariaLabel":57,"style":58},"0 0 760 290","100%","img","Sequence showing a click captured before hydration and replayed after the app hydrates.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[61,62],"rect",{"className":63,"x":65,"y":65,"width":55,"height":55,"fill":66},[64],"svg-canvas","0","#ffffff",[68,69,70],"title",{},"Event replay during hydration",[72,73,57],"desc",{},[75,76,77],"defs",{},[78,79,86],"marker",{"id":80,"viewBox":81,"refX":82,"refY":83,"markerWidth":84,"markerHeight":84,"orient":85},"faecc4b16c","0 0 10 10","9","5","7","auto-start-reverse",[87,88],"path",{"d":89,"fill":90,"style":91},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[61,93],{"x":94,"y":94,"width":95,"height":96,"rx":97,"fill":98,"stroke":90,"style":99},"1","758","288","10","none","stroke-opacity:0.18",[101,102,70],"text",{"x":103,"y":104,"fill":90,"style":105},"28.0","34.0","font-size:16px;font-weight:700",[61,107],{"x":108,"y":109,"width":110,"height":104,"rx":111,"fill":90,"stroke":90,"style":112},"41.0","56.0","150.0","6","fill-opacity:0.06;stroke-opacity:0.4",[101,114,119],{"x":115,"y":116,"fill":90,"style":117,"textAnchor":118},"116.0","78.0","font-size:13px;font-weight:700","middle","User",[61,121],{"x":122,"y":109,"width":110,"height":104,"rx":111,"fill":90,"stroke":90,"style":112},"217.0",[101,124,126],{"x":125,"y":116,"fill":90,"style":117,"textAnchor":118},"292.0","Server HTML",[61,128],{"x":129,"y":109,"width":110,"height":104,"rx":111,"fill":90,"stroke":90,"style":112},"393.0",[101,131,38],{"x":132,"y":116,"fill":90,"style":117,"textAnchor":118},"468.0",[61,134],{"x":135,"y":109,"width":110,"height":104,"rx":111,"fill":90,"stroke":90,"style":112},"569.0",[101,137,139],{"x":138,"y":116,"fill":90,"style":117,"textAnchor":118},"644.0","Angular app",[141,142],"line",{"x1":115,"y1":143,"x2":115,"y2":144,"stroke":90,"strokeWidth":145,"strokeDashArray":146,"style":148},"90.0","268.0","1.5",[147,147],"4","stroke-opacity:0.35",[141,150],{"x1":125,"y1":143,"x2":125,"y2":144,"stroke":90,"strokeWidth":145,"strokeDashArray":151,"style":148},[147,147],[141,153],{"x1":132,"y1":143,"x2":132,"y2":144,"stroke":90,"strokeWidth":145,"strokeDashArray":154,"style":148},[147,147],[141,156],{"x1":138,"y1":143,"x2":138,"y2":144,"stroke":90,"strokeWidth":145,"strokeDashArray":157,"style":148},[147,147],[141,159],{"x1":125,"y1":160,"x2":161,"y2":160,"stroke":90,"strokeWidth":145,"style":162,"markerEnd":163},"124.0","118.0","stroke-opacity:0.6","url(#faecc4b16c)",[101,165,169],{"x":166,"y":167,"fill":90,"style":168,"textAnchor":118},"204.0","117.0","font-size:12px","content visible (SSR)",[141,171],{"x1":115,"y1":172,"x2":173,"y2":172,"stroke":90,"strokeWidth":145,"style":162,"markerEnd":163},"164.0","466.0",[101,175,177],{"x":125,"y":176,"fill":90,"style":168,"textAnchor":118},"157.0","click before hydration",[141,179],{"x1":138,"y1":166,"x2":180,"y2":166,"stroke":90,"strokeWidth":145,"style":162,"markerEnd":163},"294.0",[101,182,184],{"x":132,"y":183,"fill":90,"style":168,"textAnchor":118},"197.0","hydrate (reuse DOM)",[141,186],{"x1":132,"y1":187,"x2":188,"y2":187,"stroke":90,"strokeWidth":145,"strokeDashArray":189,"style":162,"markerEnd":163},"244.0","642.0",[83,147],[101,191,194],{"x":192,"y":193,"fill":90,"style":168,"textAnchor":118},"556.0","237.0","replay click",[196,197,199],"h2",{"id":198},"rapid-diagnosis","Rapid Diagnosis",[201,202,203,218,224,230],"ul",{},[204,205,206,209,210,213,214,217],"li",{},[32,207,208],{},"Check whether SSR is enabled"," (",[45,211,212],{},"@angular\u002Fssr",") and whether ",[45,215,216],{},"provideClientHydration"," is configured.",[204,219,220,223],{},[32,221,222],{},"Look for hydration warnings"," in the browser console (NG05xx errors about mismatches).",[204,225,226,229],{},[32,227,228],{},"Check LCP"," on client-rendered routes vs server-rendered ones.",[204,231,232,235],{},[32,233,234],{},"Test early clicks",": tap buttons immediately after the page appears and see whether they respond.",[196,237,239],{"id":238},"root-cause-analysis","Root Cause Analysis",[14,241,242,245],{},[32,243,244],{},"1. Client-side rendering."," Content waits for JavaScript and data.",[14,247,248,251],{},[32,249,250],{},"2. Destructive or missing hydration."," DOM recreated, causing flicker and CLS.",[14,253,254,257],{},[32,255,256],{},"3. Hydration mismatches."," Direct DOM manipulation or browser-only code produces different DOM on server and client.",[14,259,260,263],{},[32,261,262],{},"4. Lost early interactions."," Clicks before hydration are ignored without event replay.",[196,265,267],{"id":266},"step-by-step-resolution","Step-by-Step Resolution",[269,270,272],"h3",{"id":271},"_1-add-ssr-and-hydration","1. Add SSR and hydration",[274,275,280],"pre",{"className":276,"code":277,"language":278,"meta":279,"style":279},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","ng add @angular\u002Fssr\n","bash","",[45,281,282],{"__ignoreMap":279},[283,284,286,290,294],"span",{"class":141,"line":285},1,[283,287,289],{"class":288},"sQw3B","ng",[283,291,293],{"class":292},"sZ8jY"," add",[283,295,296],{"class":292}," @angular\u002Fssr\n",[274,298,302],{"className":299,"code":300,"language":301,"meta":279,"style":279},"language-typescript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F app.config.ts\nimport { provideClientHydration, withEventReplay, withIncrementalHydration } from '@angular\u002Fplatform-browser';\nexport const appConfig = {\n  providers: [provideClientHydration(withEventReplay(), withIncrementalHydration())],\n};\n\u002F\u002F trade-off: SSR adds server cost and complexity; cache rendered pages or prerender\n\u002F\u002F static routes so the server does not render every request.\n","typescript",[45,303,304,310,330,349,373,379,385],{"__ignoreMap":279},[283,305,306],{"class":141,"line":285},[283,307,309],{"class":308},"sjfSM","\u002F\u002F app.config.ts\n",[283,311,313,317,321,324,327],{"class":141,"line":312},2,[283,314,316],{"class":315},"sPARh","import",[283,318,320],{"class":319},"saISM"," { provideClientHydration, withEventReplay, withIncrementalHydration } ",[283,322,323],{"class":315},"from",[283,325,326],{"class":292}," '@angular\u002Fplatform-browser'",[283,328,329],{"class":319},";\n",[283,331,333,336,339,343,346],{"class":141,"line":332},3,[283,334,335],{"class":315},"export",[283,337,338],{"class":315}," const",[283,340,342],{"class":341},"sPXB4"," appConfig",[283,344,345],{"class":315}," =",[283,347,348],{"class":319}," {\n",[283,350,352,355,358,361,364,367,370],{"class":141,"line":351},4,[283,353,354],{"class":319},"  providers: [",[283,356,216],{"class":357},"smZ65",[283,359,360],{"class":319},"(",[283,362,363],{"class":357},"withEventReplay",[283,365,366],{"class":319},"(), ",[283,368,369],{"class":357},"withIncrementalHydration",[283,371,372],{"class":319},"())],\n",[283,374,376],{"class":141,"line":375},5,[283,377,378],{"class":319},"};\n",[283,380,382],{"class":141,"line":381},6,[283,383,384],{"class":308},"\u002F\u002F trade-off: SSR adds server cost and complexity; cache rendered pages or prerender\n",[283,386,388],{"class":141,"line":387},7,[283,389,390],{"class":308},"\u002F\u002F static routes so the server does not render every request.\n",[269,392,394],{"id":393},"_2-fix-browser-only-code","2. Fix browser-only code",[14,396,397,398,401,402,401,405,408,409,412,413,416],{},"Move code that uses ",[45,399,400],{},"window",", ",[45,403,404],{},"document",[45,406,407],{},"localStorage"," or measures layout into ",[45,410,411],{},"afterNextRender"," or ",[45,414,415],{},"afterRender",", which run only in the browser after rendering.",[269,418,420],{"id":419},"_3-avoid-direct-dom-manipulation","3. Avoid direct DOM manipulation",[14,422,423,424,427,428,431],{},"Replace ",[45,425,426],{},"ElementRef.nativeElement"," manipulation and third-party DOM libraries during render with Angular bindings, or mark components with ",[45,429,430],{},"ngSkipHydration"," as a last resort.",[269,433,435],{"id":434},"_4-transfer-data-to-avoid-refetching","4. Transfer data to avoid refetching",[14,437,438,441],{},[45,439,440],{},"HttpClient"," responses made during SSR are cached and transferred to the client by default with hydration, so the client does not repeat requests.",[14,443,444],{},[52,445,59,448,59,451,59,454,59,456,59,459,59,461,59,469,59,476,59,483,59,488,59,492,59,498,59,502,59,506,59,512,59,516,59],{"viewBox":446,"width":55,"role":56,"ariaLabel":447,"style":58},"0 0 760 185","Bar chart comparing LCP for client-side rendering, SSR with destructive hydration and SSR with non-destructive hydration.",[61,449],{"className":450,"x":65,"y":65,"width":55,"height":55,"fill":66},[64],[68,452,453],{},"LCP p75 on a landing page by rendering approach",[72,455,447],{},[61,457],{"x":94,"y":94,"width":95,"height":458,"rx":97,"fill":98,"stroke":90,"style":99},"183",[101,460,453],{"x":103,"y":104,"fill":90,"style":105},[141,462],{"x1":463,"y1":464,"x2":463,"y2":465,"stroke":466,"strokeWidth":145,"strokeDashArray":467,"style":468},"509.9","72.0","163.0","#b8860b",[147,147],"stroke-opacity:0.9",[101,470,475],{"x":471,"y":472,"fill":90,"style":473,"textAnchor":474},"215.7","92.0","font-size:13px","end","Client-side rendering",[61,477],{"x":478,"y":116,"width":479,"height":480,"rx":481,"fill":98,"stroke":90,"style":482},"227.7","440.3","19","3","fill-opacity:0.7;stroke-opacity:0.35",[101,484,487],{"x":485,"y":472,"fill":90,"style":486},"674.0","font-size:12px;font-weight:600","3900ms",[101,489,491],{"x":471,"y":490,"fill":90,"style":473,"textAnchor":474},"123.0","SSR + destructive hydration",[61,493],{"x":478,"y":494,"width":495,"height":480,"rx":481,"fill":496,"stroke":496,"style":497},"109.0","259.7","#0466c8","fill-opacity:0.55;stroke-opacity:0.9",[101,499,501],{"x":500,"y":490,"fill":90,"style":486},"493.4","2300ms",[101,503,505],{"x":471,"y":504,"fill":90,"style":473,"textAnchor":474},"154.0","SSR + non-destructive hydration",[61,507],{"x":478,"y":508,"width":509,"height":480,"rx":481,"fill":510,"stroke":466,"style":511},"140.0","214.5","#ffc300","fill-opacity:0.7;stroke-opacity:0.9",[101,513,515],{"x":514,"y":504,"fill":90,"style":486},"448.2","1900ms",[101,517,521],{"x":463,"y":518,"fill":519,"style":520,"textAnchor":118},"68.0","#8a6608","font-size:12px;font-weight:700","LCP good",[196,523,525],{"id":524},"verification","Verification",[14,527,528],{},"Load the page and check the console for hydration messages (Angular logs hydration statistics in development). The DOM should not flicker after bootstrap. Click buttons immediately after content appears; with event replay, actions should complete after hydration. In the field, LCP should improve for SSR routes, and CLS should not increase.",[196,530,532],{"id":531},"worked-example-a-travel-booking-landing-page","Worked Example: A Travel Booking Landing Page",[14,534,535,536,538],{},"A travel company's Angular landing pages were client-rendered: LCP p75 on mobile was 3.9 seconds. Enabling SSR with destructive hydration (an older setup) improved LCP to 2.3 seconds but caused a visible flash as the app re-rendered, and CLS rose to 0.12. Upgrading to non-destructive hydration with event replay removed the flash (CLS back to 0.02) and improved LCP to 1.9 seconds because the browser no longer repainted the hero. Search form taps during the first seconds — previously ignored — were replayed, and form start rate rose 6%. Two components using a jQuery date picker needed ",[45,537,430],{}," until replaced.",[196,540,542],{"id":541},"hydration-mismatches-and-how-to-find-them","Hydration Mismatches and How to Find Them",[14,544,545,546,549,550,552,553,555,556,558],{},"A mismatch occurs when the client expects different DOM than the server produced: different text (dates formatted with different time zones), elements added only on the client, invalid HTML that the browser parser corrects (a ",[45,547,548],{},"div"," inside a ",[45,551,14],{},"), or third-party scripts that modify the DOM before hydration. Angular reports mismatches with error codes and the affected component in development. Fix the cause: render consistent data on both sides (pass server-computed values via transfer state), keep HTML valid, and defer browser-only rendering to ",[45,554,411],{},". ",[45,557,430],{}," makes a component re-render on the client instead of hydrating; use it sparingly.",[14,560,561],{},[52,562,59,565,59,568,59,571,59,573,59,576,59,578,59,582,59,589,59,593,59,597,59,600,59,604,59,607,59,611,59,613,59,616,59,618,59,621,59,623,59,627,59,629,59,632,59,634,59,636,59,639,59,643,59,645,59,648,59,650,59,653,59,656,59,660,59,662,59,665,59,667,59],{"viewBox":563,"width":55,"role":56,"ariaLabel":564,"style":58},"0 0 760 228","Typical causes of Angular hydration mismatches and how to fix them.",[61,566],{"className":567,"x":65,"y":65,"width":55,"height":55,"fill":66},[64],[68,569,570],{},"Common hydration mismatch causes",[72,572,564],{},[61,574],{"x":94,"y":94,"width":95,"height":575,"rx":97,"fill":98,"stroke":90,"style":99},"226",[101,577,570],{"x":103,"y":104,"fill":90,"style":105},[61,579],{"x":103,"y":109,"width":580,"height":581,"rx":65,"fill":90,"stroke":90,"style":112},"202.5","30.0",[101,583,588],{"x":584,"y":585,"fill":90,"style":586,"textAnchor":587},"38.0","75.5","font-size:12.5px;font-weight:700","start","Cause",[61,590],{"x":591,"y":109,"width":592,"height":581,"rx":65,"fill":90,"stroke":90,"style":112},"230.5","250.8",[101,594,596],{"x":595,"y":585,"fill":90,"style":586,"textAnchor":118},"355.9","Symptom",[61,598],{"x":599,"y":109,"width":592,"height":581,"rx":65,"fill":90,"stroke":90,"style":112},"481.2",[101,601,603],{"x":602,"y":585,"fill":90,"style":586,"textAnchor":118},"606.6","Fix",[61,605],{"x":103,"y":606,"width":580,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},"86.0",[101,608,610],{"x":584,"y":609,"fill":90,"style":586,"textAnchor":587},"105.5","Dates or random values",[61,612],{"x":591,"y":606,"width":592,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},[101,614,615],{"x":595,"y":609,"fill":90,"style":168,"textAnchor":118},"text differs",[61,617],{"x":599,"y":606,"width":592,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},[101,619,620],{"x":602,"y":609,"fill":90,"style":168,"textAnchor":118},"compute once on server and transfer",[61,622],{"x":103,"y":115,"width":580,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},[101,624,626],{"x":584,"y":625,"fill":90,"style":586,"textAnchor":587},"135.5","Browser-only APIs in templates",[61,628],{"x":591,"y":115,"width":592,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},[101,630,631],{"x":595,"y":625,"fill":90,"style":168,"textAnchor":118},"server errors or missing nodes",[61,633],{"x":599,"y":115,"width":592,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},[101,635,411],{"x":602,"y":625,"fill":90,"style":168,"textAnchor":118},[61,637],{"x":103,"y":638,"width":580,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},"146.0",[101,640,642],{"x":584,"y":641,"fill":90,"style":586,"textAnchor":587},"165.5","Invalid HTML nesting",[61,644],{"x":591,"y":638,"width":592,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},[101,646,647],{"x":595,"y":641,"fill":90,"style":168,"textAnchor":118},"DOM restructured by parser",[61,649],{"x":599,"y":638,"width":592,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},[101,651,652],{"x":602,"y":641,"fill":90,"style":168,"textAnchor":118},"fix markup",[61,654],{"x":103,"y":655,"width":580,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},"176.0",[101,657,659],{"x":584,"y":658,"fill":90,"style":586,"textAnchor":587},"195.5","Third-party DOM libraries",[61,661],{"x":591,"y":655,"width":592,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},[101,663,664],{"x":595,"y":658,"fill":90,"style":168,"textAnchor":118},"unexpected nodes",[61,666],{"x":599,"y":655,"width":592,"height":581,"rx":65,"fill":98,"stroke":90,"style":148},[101,668,669],{"x":602,"y":658,"fill":90,"style":168,"textAnchor":118},"replace or ngSkipHydration",[196,671,673],{"id":672},"caching-ssr-output","Caching SSR Output",[14,675,676],{},"SSR moves rendering cost to the server. For routes whose content is the same for many users, prerender them at build time (Angular supports route prerendering) or cache rendered HTML at the CDN. Personalised routes can render a shared shell on the server and load personal data on the client after hydration. Without caching, SSR improves LCP but can make TTFB the new bottleneck under load.",[196,678,680],{"id":679},"rolling-out-ssr-safely","Rolling Out SSR Safely",[14,682,683],{},"Introduce SSR route by route. Start with public, mostly static pages (landing, marketing, content), where the LCP gain is largest and personalisation is minimal. Run the app with hydration in development and fix every mismatch warning before enabling it in production. Monitor server error rates and TTFB after launch, since server rendering introduces new failure modes (missing browser APIs, timeouts in data fetching). Then extend SSR to more routes, adding prerendering or caching where content allows.",[196,685,687],{"id":686},"common-mistakes","Common Mistakes",[201,689,690,696,702,708],{},[204,691,692,695],{},[32,693,694],{},"Using window or document during render."," Errors on the server or mismatches.",[204,697,698,701],{},[32,699,700],{},"Overusing ngSkipHydration."," Re-rendering defeats hydration's benefits.",[204,703,704,707],{},[32,705,706],{},"No event replay."," Early clicks are lost.",[204,709,710,713],{},[32,711,712],{},"Uncached SSR for static pages."," Server cost and slower TTFB.",[196,715,717],{"id":716},"edge-cases","Edge Cases",[14,719,720,723],{},[32,721,722],{},"i18n."," Locale-specific rendering must match between server and client; use the same locale data.",[14,725,726,729],{},[32,727,728],{},"Authentication."," Server rendering with user-specific data must avoid caching personal pages.",[14,731,732,735],{},[32,733,734],{},"Zoneless."," Hydration works without zone.js; mark pending async tasks so SSR waits for data.",[14,737,738,741],{},[32,739,740],{},"Streaming."," Angular SSR renders full pages; combine with caching and fast data sources for good TTFB.",[196,743,745],{"id":744},"faq","FAQ",[747,748,751,755],"details",{"className":749},[750],"faq-item",[752,753,754],"summary",{},"What is non-destructive hydration?",[14,756,757],{},"Hydration that reuses the server-rendered DOM, attaching Angular to existing nodes instead of re-creating them.",[747,759,761,764],{"className":760},[750],[752,762,763],{},"What does event replay do?",[14,765,766],{},"It records user events that occur before hydration finishes and replays them afterwards, so early interactions are not lost.",[747,768,770,773],{"className":769},[750],[752,771,772],{},"How do I fix hydration errors?",[14,774,775,776,778],{},"Read the error code and component in the console, then make server and client render the same DOM: consistent data, valid HTML, browser-only code in ",[45,777,411],{},".",[747,780,782,785],{"className":781},[750],[752,783,784],{},"Does SSR improve INP?",[14,786,787],{},"Indirectly at best. SSR mainly improves LCP. INP depends on hydration cost and interaction handling; incremental hydration and zoneless help there.",[747,789,791,794],{"className":790},[750],[752,792,793],{},"What is ngSkipHydration?",[14,795,796],{},"An attribute that tells Angular to re-render a component on the client instead of hydrating it, as a workaround for components that cannot hydrate.",[747,798,800,803],{"className":799},[750],[752,801,802],{},"Should every route be server-rendered?",[14,804,805],{},"Public routes benefit most. Private, highly interactive routes may be fine client-rendered, and static routes should be prerendered.",[747,807,809,812],{"className":808},[750],[752,810,811],{},"Does HttpClient refetch data after SSR?",[14,813,814],{},"Not by default with hydration: responses are transferred from the server and reused on the client.",[747,816,818,821],{"className":817},[750],[752,819,820],{},"Can I prerender Angular routes?",[14,822,823],{},"Yes. Angular supports prerendering routes at build time, which gives static HTML with the same hydration behaviour.",[747,825,827,830],{"className":826},[750],[752,828,829],{},"Does event replay work for all events?",[14,831,832],{},"It covers common user events such as clicks and inputs. Check the documentation for the event types supported in your Angular version.",[747,834,836,839],{"className":835},[750],[752,837,838],{},"How do I know hydration succeeded?",[14,840,841],{},"In development, Angular logs hydration statistics (components hydrated, skipped) to the console; mismatches appear as errors with codes.",[747,843,845,848],{"className":844},[750],[752,846,847],{},"Is SSR required for good Angular performance?",[14,849,850],{},"Not for private app screens, but for public pages it is the most reliable way to get a good LCP.",[196,852,854],{"id":853},"related","Related",[201,856,857,864,871],{},[204,858,859,863],{},[18,860,862],{"href":861},"\u002Fframework-performance\u002Fangular-performance\u002Fdeferring-components-with-angular-defer-blocks\u002F","Deferring components with Angular @defer blocks"," — incremental hydration triggers.",[204,865,866,870],{},[18,867,869],{"href":868},"\u002Fframework-performance\u002Fangular-performance\u002Fgoing-zoneless-in-angular\u002F","Going zoneless in Angular"," — change detection after hydration.",[204,872,873,877],{},[18,874,876],{"href":875},"\u002Fnetwork-protocol-optimization\u002Ftime-to-first-byte-optimization\u002F","Time to First Byte (TTFB) optimization"," — keeping SSR responses fast.",[879,880,882],"script",{"type":881},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Angular SSR Hydration and Event Replay\",\n  \"description\": \"How Angular server rendering, non-destructive hydration and event replay work together, and how to adopt them without hydration errors.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Add SSR and hydration\",\n      \"text\": \"Add SSR and hydration\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Fix browser-only code\",\n      \"text\": \"Move code that uses window, document, localStorage or measures layout into afterNextRender or afterRender, which run only in the browser after rendering.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Avoid direct DOM manipulation\",\n      \"text\": \"Replace ElementRef.nativeElement manipulation and third-party DOM libraries during render with Angular bindings, or mark components with ngSkipHydration as a last resort.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Transfer data to avoid refetching\",\n      \"text\": \"HttpClient responses made during SSR are cached and transferred to the client by default with hydration, so the client does not repeat requests.\"\n    }\n  ]\n}\n",[879,884,885],{"type":881},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Angular SSR Hydration and Event Replay\",\n  \"description\": \"How Angular server rendering, non-destructive hydration and event replay work together, and how to adopt them without hydration errors.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fframework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay\u002F\"\n  }\n}\n",[879,887,888],{"type":881},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"Framework Performance\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fframework-performance\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Angular Performance\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fframework-performance\u002Fangular-performance\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Angular SSR Hydration and Event Replay\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fframework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay\u002F\"\n    }\n  ]\n}\n",[890,891,892],"style",{},"html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}",{"title":279,"searchDepth":312,"depth":312,"links":894},[895,896,897,903,904,905,906,907,908,909,910,911],{"id":198,"depth":312,"text":199},{"id":238,"depth":312,"text":239},{"id":266,"depth":312,"text":267,"children":898},[899,900,901,902],{"id":271,"depth":332,"text":272},{"id":393,"depth":332,"text":394},{"id":419,"depth":332,"text":420},{"id":434,"depth":332,"text":435},{"id":524,"depth":312,"text":525},{"id":531,"depth":312,"text":532},{"id":541,"depth":312,"text":542},{"id":672,"depth":312,"text":673},{"id":679,"depth":312,"text":680},{"id":686,"depth":312,"text":687},{"id":716,"depth":312,"text":717},{"id":744,"depth":312,"text":745},{"id":853,"depth":312,"text":854},"How Angular server rendering, non-destructive hydration and event replay work together, and how to adopt them without hydration errors.","md",{"slug":12,"type":915,"breadcrumb":916,"datePublished":924,"dateModified":924},"article",[917,920,921,922],{"name":918,"url":919},"Home","\u002F",{"name":26,"url":25},{"name":21,"url":20},{"name":5,"url":923},"\u002Fframework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay\u002F","2026-10-06",true,"\u002Fframework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay",{"title":928,"description":929},"Angular SSR Hydration and Event Replay Explained","Enable Angular SSR with non-destructive hydration and event replay for fast LCP without flicker or lost clicks; fix hydration mismatches and browser-only code.","framework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay\u002Findex","IhgLhw5_StDmg8sgOifM2UO39HuVGsLugQL55UyP1cI",[933,936],{"title":21,"path":934,"stem":935,"children":-1},"\u002Fframework-performance\u002Fangular-performance","framework-performance\u002Fangular-performance\u002Findex",{"title":937,"path":938,"stem":939,"children":-1},"Deferring Components with Angular @defer Blocks","\u002Fframework-performance\u002Fangular-performance\u002Fdeferring-components-with-angular-defer-blocks","framework-performance\u002Fangular-performance\u002Fdeferring-components-with-angular-defer-blocks\u002Findex",1791308074703]