[{"data":1,"prerenderedAt":1188},["ShallowReactive",2],{"content:\u002Fframework-performance\u002Fangular-performance":3,"surroundings:\u002Fframework-performance\u002Fangular-performance":1180},{"id":4,"title":5,"body":6,"description":1161,"extension":1162,"meta":1163,"navigation":1173,"path":1174,"seo":1175,"stem":1178,"__hash__":1179},"content\u002Fframework-performance\u002Fangular-performance\u002Findex.md","Angular Performance",{"type":7,"value":8,"toc":1137},"minimark",[9,14,29,52,154,159,178,182,199,203,206,210,213,217,223,227,240,371,433,437,443,450,454,471,481,499,509,515,519,529,581,585,594,598,611,615,629,747,751,768,772,796,799,803,818,822,829,833,850,854,889,893,925,929,941,950,959,968,977,986,995,1007,1021,1030,1042,1051,1060,1069,1073,1096,1100,1122,1127,1130,1133],[10,11,13],"h1",{"id":12},"angular-performance-targeted-updates-and-less-upfront-work","Angular Performance: Targeted Updates and Less Upfront Work",[15,16,17,18,23,24,28],"p",{},"This topic is part of ",[19,20,22],"a",{"href":21},"\u002Fframework-performance\u002F","Framework Performance",". Angular has changed substantially in recent versions. Standalone components replaced NgModules as the default, signals introduced fine-grained reactivity, zoneless change detection removed the need for zone.js, ",[25,26,27],"code",{},"@defer"," blocks made lazy loading of components declarative, and server-side rendering gained non-destructive hydration with event replay and incremental hydration. Applications built with these features can be fast by default; applications built on older patterns often carry avoidable costs.",[15,30,31,32,36,37,40,41,44,45,48,49,51],{},"The classic Angular performance problems come from ",[33,34,35],"strong",{},"change detection",": with zone.js, every event, timer and HTTP response triggers a check of the whole component tree (unless components use ",[25,38,39],{},"OnPush","), so large trees and frequent events cost main-thread time. Then there is ",[33,42,43],{},"bundle size"," — Angular apps ship a framework runtime plus application code, and eagerly loaded features grow the main bundle — and ",[33,46,47],{},"client-side rendering",", where content waits for JavaScript. Modern Angular addresses each: signals and zoneless for targeted updates, ",[25,50,27],{}," and lazy routes for smaller initial bundles, and SSR with hydration for fast first paint.",[15,53,54],{},[55,56,62,63,62,70,62,74,62,77,62,86,62,92,62,101,62,107,62,112,62,117,62,120,62,123,62,126,62,129,62,134,62,138,62,140,62,144,62,146,62,149,62,151,62],"svg",{"viewBox":57,"width":58,"role":59,"ariaLabel":60,"style":61},"0 0 760 204","100%","img","Comparison of default zone.js change detection and signal-based zoneless updates in Angular.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[64,65],"rect",{"className":66,"x":68,"y":68,"width":58,"height":58,"fill":69},[67],"svg-canvas","0","#ffffff",[71,72,73],"title",{},"Zone.js change detection vs signals and zoneless",[75,76,60],"desc",{},[64,78],{"x":79,"y":79,"width":80,"height":81,"rx":82,"fill":83,"stroke":84,"style":85},"1","758","202","10","none","currentColor","stroke-opacity:0.18",[87,88,73],"text",{"x":89,"y":90,"fill":84,"style":91},"28.0","34.0","font-size:16px;font-weight:700",[64,93],{"x":89,"y":94,"width":95,"height":96,"rx":97,"fill":98,"stroke":99,"style":100},"56.0","340.0","126.0","6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[87,102,106],{"x":103,"y":104,"fill":84,"style":105},"42.0","82.0","font-size:14px;font-weight:700","zone.js (default CD)",[87,108,111],{"x":103,"y":109,"fill":84,"style":110},"108.0","font-size:12.5px;font-weight:700","•",[87,113,116],{"x":94,"y":109,"fill":84,"style":114,"textAnchor":115},"font-size:12.5px","start","Any async event triggers a check",[87,118,111],{"x":103,"y":119,"fill":84,"style":110},"132.0",[87,121,122],{"x":94,"y":119,"fill":84,"style":114,"textAnchor":115},"Whole tree checked (unless OnPush)",[87,124,111],{"x":103,"y":125,"fill":84,"style":110},"156.0",[87,127,128],{"x":94,"y":125,"fill":84,"style":114,"textAnchor":115},"zone.js patches browser APIs (bundle + overhead)",[64,130],{"x":131,"y":94,"width":95,"height":96,"rx":97,"fill":132,"stroke":132,"style":133},"392.0","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[87,135,137],{"x":136,"y":104,"fill":84,"style":105},"406.0","Signals + zoneless",[87,139,111],{"x":136,"y":109,"fill":84,"style":110},[87,141,143],{"x":142,"y":109,"fill":84,"style":114,"textAnchor":115},"420.0","Updates scheduled when signals change",[87,145,111],{"x":136,"y":119,"fill":84,"style":110},[87,147,148],{"x":142,"y":119,"fill":84,"style":114,"textAnchor":115},"Only affected views refresh",[87,150,111],{"x":136,"y":125,"fill":84,"style":110},[87,152,153],{"x":142,"y":125,"fill":84,"style":114,"textAnchor":115},"No zone.js in the bundle",[155,156,158],"h2",{"id":157},"the-metric-degradation-this-topic-addresses","The Metric Degradation This Topic Addresses",[15,160,161,162,165,166,169,170,173,174,177],{},"Excessive change detection shows up as long tasks during interactions and timers, hurting ",[33,163,164],{},"INP"," — especially on pages with large component trees, frequent events (scroll, mousemove, websockets) or third-party scripts that trigger zone-patched callbacks. Large main bundles and client-side rendering hurt ",[33,167,168],{},"LCP"," and ",[33,171,172],{},"TBT",". Destructive hydration in older Angular versions (re-rendering the DOM on bootstrap) caused flicker and ",[33,175,176],{},"CLS","; modern non-destructive hydration avoids it.",[155,179,181],{"id":180},"prerequisites","Prerequisites",[183,184,185,189,196],"ul",{},[186,187,188],"li",{},"Angular DevTools (with the profiler) and the browser Performance panel.",[186,190,191,192,195],{},"Build stats (",[25,193,194],{},"ng build --stats-json",") and a bundle analyser such as source-map-explorer or esbuild's metafile analyser.",[186,197,198],{},"Field metrics per route.",[155,200,202],{"id":201},"_1-environment-setup-profile-change-detection","1. Environment Setup: Profile Change Detection",[15,204,205],{},"Angular DevTools' profiler records change detection cycles, showing which components were checked and how long each took. Record a typical interaction and note how many cycles run and how long each takes.",[155,207,209],{"id":208},"_2-capture-a-baseline","2. Capture a Baseline",[15,211,212],{},"Record initial bundle size, LCP and INP per route in the field, and change detection time for key interactions in the lab with CPU throttling.",[155,214,216],{"id":215},"_3-isolate-causes","3. Isolate Causes",[15,218,219,220,222],{},"Frequent change detection cycles → zone.js triggering on events that do not change state (scroll handlers, polling, third-party timers). Long cycles → large trees without ",[25,221,39],{},", expensive template expressions, functions called in templates. Large initial bundles → eagerly imported features, heavy libraries. Slow LCP → client-side rendering or late-loading hero content.",[155,224,226],{"id":225},"_4-apply-modern-angular-patterns","4. Apply Modern Angular Patterns",[15,228,229,230,232,233,235,236,239],{},"Adopt signals for component state and ",[25,231,39],{}," (or zoneless) change detection. Defer heavy, below-the-fold or interaction-only components with ",[25,234,27],{},". Lazy-load routes. Enable SSR with hydration and event replay. Use ",[25,237,238],{},"NgOptimizedImage"," for the LCP image.",[241,242,247],"pre",{"className":243,"code":244,"language":245,"meta":246,"style":246},"language-typescript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F app.config.ts — zoneless change detection with SSR hydration.\nimport { provideExperimentalZonelessChangeDetection } from '@angular\u002Fcore';\nimport { provideClientHydration, withEventReplay } from '@angular\u002Fplatform-browser';\nexport const appConfig = {\n  providers: [\n    provideExperimentalZonelessChangeDetection(),   \u002F\u002F name varies by Angular version\n    provideClientHydration(withEventReplay()),\n  ],\n};\n\u002F\u002F trade-off: zoneless requires state changes to notify Angular (signals, async pipe,\n\u002F\u002F markForCheck); code relying on zone.js to detect mutations must be updated first.\n","typescript","",[25,248,249,258,279,294,313,319,332,347,353,359,365],{"__ignoreMap":246},[250,251,254],"span",{"class":252,"line":253},"line",1,[250,255,257],{"class":256},"sjfSM","\u002F\u002F app.config.ts — zoneless change detection with SSR hydration.\n",[250,259,261,265,269,272,276],{"class":252,"line":260},2,[250,262,264],{"class":263},"sPARh","import",[250,266,268],{"class":267},"saISM"," { provideExperimentalZonelessChangeDetection } ",[250,270,271],{"class":263},"from",[250,273,275],{"class":274},"sZ8jY"," '@angular\u002Fcore'",[250,277,278],{"class":267},";\n",[250,280,282,284,287,289,292],{"class":252,"line":281},3,[250,283,264],{"class":263},[250,285,286],{"class":267}," { provideClientHydration, withEventReplay } ",[250,288,271],{"class":263},[250,290,291],{"class":274}," '@angular\u002Fplatform-browser'",[250,293,278],{"class":267},[250,295,297,300,303,307,310],{"class":252,"line":296},4,[250,298,299],{"class":263},"export",[250,301,302],{"class":263}," const",[250,304,306],{"class":305},"sPXB4"," appConfig",[250,308,309],{"class":263}," =",[250,311,312],{"class":267}," {\n",[250,314,316],{"class":252,"line":315},5,[250,317,318],{"class":267},"  providers: [\n",[250,320,322,326,329],{"class":252,"line":321},6,[250,323,325],{"class":324},"smZ65","    provideExperimentalZonelessChangeDetection",[250,327,328],{"class":267},"(),   ",[250,330,331],{"class":256},"\u002F\u002F name varies by Angular version\n",[250,333,335,338,341,344],{"class":252,"line":334},7,[250,336,337],{"class":324},"    provideClientHydration",[250,339,340],{"class":267},"(",[250,342,343],{"class":324},"withEventReplay",[250,345,346],{"class":267},"()),\n",[250,348,350],{"class":252,"line":349},8,[250,351,352],{"class":267},"  ],\n",[250,354,356],{"class":252,"line":355},9,[250,357,358],{"class":267},"};\n",[250,360,362],{"class":252,"line":361},10,[250,363,364],{"class":256},"\u002F\u002F trade-off: zoneless requires state changes to notify Angular (signals, async pipe,\n",[250,366,368],{"class":252,"line":367},11,[250,369,370],{"class":256},"\u002F\u002F markForCheck); code relying on zone.js to detect mutations must be updated first.\n",[15,372,373],{},[55,374,62,377,62,380,62,383,62,385,62,388,62,390,62,397,62,404,62,409,62,413,62,418,62,422,62,424,62,429,62],{"viewBox":375,"width":58,"role":59,"ariaLabel":376,"style":61},"0 0 760 163","Bar chart comparing change detection time for a dashboard under default, OnPush and zoneless signal-based change detection.",[64,378],{"className":379,"x":68,"y":68,"width":58,"height":58,"fill":69},[67],[71,381,382],{},"Change detection time per scroll-triggered cycle (dashboard)",[75,384,376],{},[64,386],{"x":79,"y":79,"width":80,"height":387,"rx":82,"fill":83,"stroke":84,"style":85},"161",[87,389,382],{"x":89,"y":90,"fill":84,"style":91},[87,391,396],{"x":392,"y":393,"fill":84,"style":394,"textAnchor":395},"145.4","70.0","font-size:13px","end","Default CD (zone.js)",[64,398],{"x":399,"y":94,"width":400,"height":401,"rx":402,"fill":83,"stroke":84,"style":403},"157.4","510.6","19","3","fill-opacity:0.7;stroke-opacity:0.35",[87,405,408],{"x":406,"y":393,"fill":84,"style":407},"674.0","font-size:12px;font-weight:600","38ms",[87,410,412],{"x":392,"y":411,"fill":84,"style":394,"textAnchor":395},"101.0","OnPush everywhere",[64,414],{"x":399,"y":415,"width":416,"height":401,"rx":402,"fill":132,"stroke":132,"style":417},"87.0","120.9","fill-opacity:0.55;stroke-opacity:0.9",[87,419,421],{"x":420,"y":411,"fill":84,"style":407},"284.3","9ms",[87,423,137],{"x":392,"y":119,"fill":84,"style":394,"textAnchor":395},[64,425],{"x":399,"y":426,"width":427,"height":401,"rx":402,"fill":98,"stroke":99,"style":428},"118.0","26.9","fill-opacity:0.7;stroke-opacity:0.9",[87,430,432],{"x":431,"y":119,"fill":84,"style":407},"190.2","2ms",[155,434,436],{"id":435},"deconstructing-angular-change-detection","Deconstructing Angular Change Detection",[15,438,439,440,442],{},"With zone.js, Angular wraps browser APIs (events, timers, promises, XHR) so that after any async callback, it runs change detection: it walks the component tree and re-evaluates template bindings, updating the DOM where values changed. Default components are checked on every cycle; ",[25,441,39],{}," components are checked only when their inputs change by reference, an event originates inside them, or they are explicitly marked. On large pages, default change detection on every scroll or timer event adds up.",[15,444,445,446,449],{},"Signals change the model. A signal is a reactive value; templates that read signals register dependencies, and when a signal changes, Angular knows exactly which views need refreshing. With zoneless change detection, there is no zone.js; Angular schedules change detection only when signals change, when ",[25,447,448],{},"markForCheck"," is called, or for events bound in templates. This makes updates targeted and removes zone.js patching overhead and its bundle size.",[155,451,453],{"id":452},"advanced-diagnostics-and-edge-cases","Advanced Diagnostics and Edge Cases",[15,455,456,62,459,462,463,466,467,470],{},[33,457,458],{},"Functions in templates.",[25,460,461],{},"{{ total() }}"," where ",[25,464,465],{},"total"," is a regular method re-runs on every check; use signals with ",[25,468,469],{},"computed"," or pure pipes.",[15,472,473,476,477,480],{},[33,474,475],{},"Third-party libraries and zones."," Libraries that use timers or event listeners trigger change detection; run them outside Angular (",[25,478,479],{},"NgZone.runOutsideAngular",") when using zone.js.",[15,482,483,494,495,498],{},[33,484,485,486,489,490,493],{},"Large ",[25,487,488],{},"*ngFor","\u002F",[25,491,492],{},"@for"," lists."," Always use ",[25,496,497],{},"track"," with a stable identity; virtualize long lists with the CDK virtual scroll.",[15,500,501,504,505,508],{},[33,502,503],{},"Hydration mismatches."," Direct DOM manipulation or browser-only APIs during render break hydration; use ",[25,506,507],{},"afterNextRender"," for browser-only code.",[15,510,511,514],{},[33,512,513],{},"Polyfills."," Remove polyfills for browsers you no longer support; zone.js itself is removed with zoneless.",[155,516,518],{"id":517},"validation-and-budgeting","Validation and Budgeting",[15,520,521,522,62,525,528],{},"Use Angular's build budgets (",[25,523,524],{},"angular.json",[25,526,527],{},"budgets",") to fail builds when initial bundles exceed limits. In the lab, compare change detection cycles and durations for key interactions. In the field, track LCP and INP per route.",[530,531,532,545],"table",{},[533,534,535],"thead",{},[536,537,538,542],"tr",{},[539,540,541],"th",{},"Budget",[539,543,544],{},"Target",[546,547,548,557,565,573],"tbody",{},[536,549,550,554],{},[551,552,553],"td",{},"Initial bundle (compressed)",[551,555,556],{},"\u003C 200 KB",[536,558,559,562],{},[551,560,561],{},"Change detection per interaction (4x CPU)",[551,563,564],{},"\u003C 16 ms",[536,566,567,570],{},[551,568,569],{},"Lazy routes",[551,571,572],{},"All non-landing features",[536,574,575,578],{},[551,576,577],{},"SSR + hydration",[551,579,580],{},"Enabled for public routes",[155,582,584],{"id":583},"worked-example-an-insurance-quote-application","Worked Example: An Insurance Quote Application",[15,586,587,588,590,591,593],{},"An insurance company's quote application was a large Angular app with default change detection, client-side rendering and a 1.1MB (uncompressed) main bundle. LCP p75 on mobile was 4.0 seconds and INP p75 320ms; the quote form ran change detection on every keystroke across 900 components. The team enabled SSR with hydration for the landing and quote-start pages, moved the form to signals with ",[25,589,39],{}," components, deferred the comparison table and help chat with ",[25,592,27],{},", and lazy-loaded the account area. Initial bundle dropped to 420KB uncompressed, LCP p75 to 2.1 seconds, and INP p75 to 140ms. A later move to zoneless removed zone.js and another 30KB.",[155,595,597],{"id":596},"bundle-size-in-angular","Bundle Size in Angular",[15,599,600,601,169,604,607,608,610],{},"The Angular CLI's esbuild-based builder produces efficient bundles, but application structure determines what is in the initial chunk. Every route not lazy-loaded, every component referenced from the root, and every library imported in eagerly loaded code ends up there. Use ",[25,602,603],{},"loadComponent",[25,605,606],{},"loadChildren"," for routes, ",[25,609,27],{}," for components within pages, and check the build stats for large dependencies. Angular's build budgets give immediate feedback in CI when the initial bundle grows.",[155,612,614],{"id":613},"server-rendering-hydration-and-incremental-hydration","Server Rendering, Hydration and Incremental Hydration",[15,616,617,618,620,621,624,625,628],{},"Angular SSR renders pages on the server for fast first paint. Non-destructive hydration reuses the server DOM instead of re-creating it, avoiding flicker and CLS. Event replay records user interactions that happen before hydration completes and replays them afterwards, so early clicks are not lost. Incremental hydration combines ",[25,619,27],{}," with hydration triggers (",[25,622,623],{},"hydrate on viewport",", ",[25,626,627],{},"hydrate on interaction","), leaving parts of the server-rendered page dehydrated until needed — reducing the JavaScript that runs at load, similar to islands in other frameworks.",[15,630,631],{},[55,632,62,635,62,638,62,641,62,643,62,660,62,663,62,665,62,668,62,675,62,680,62,683,62,687,62,690,62,693,62,697,62,700,62,703,62,707,62,710,62,713,62,717,62,720,62,724,62,727,62,735,62,739,62,743,62],{"viewBox":633,"width":58,"role":59,"ariaLabel":634,"style":61},"0 0 760 165","Steps from server rendering through hydration with event replay and incremental hydration of deferred blocks.",[64,636],{"className":637,"x":68,"y":68,"width":58,"height":58,"fill":69},[67],[71,639,640],{},"Angular SSR lifecycle with incremental hydration",[75,642,634],{},[644,645,646],"defs",{},[647,648,655],"marker",{"id":649,"viewBox":650,"refX":651,"refY":652,"markerWidth":653,"markerHeight":653,"orient":654},"fa8199c3f3","0 0 10 10","9","5","7","auto-start-reverse",[656,657],"path",{"d":658,"fill":84,"style":659},"M0 0 L10 5 L0 10 z","fill-opacity:0.7",[64,661],{"x":79,"y":79,"width":80,"height":662,"rx":82,"fill":83,"stroke":84,"style":85},"163",[87,664,640],{"x":89,"y":90,"fill":84,"style":91},[64,666],{"x":89,"y":94,"width":667,"height":415,"rx":97,"fill":132,"stroke":132,"style":133},"108.8",[87,669,674],{"x":670,"y":671,"fill":84,"style":672,"textAnchor":673},"82.4","78.0","font-size:13px;font-weight:700","middle","Server render",[87,676,679],{"x":670,"y":677,"fill":84,"style":678,"textAnchor":673},"96.0","font-size:12px","full HTML",[64,681],{"x":682,"y":94,"width":667,"height":415,"rx":97,"fill":132,"stroke":132,"style":133},"176.8",[87,684,686],{"x":685,"y":671,"fill":84,"style":672,"textAnchor":673},"231.2","Paint",[87,688,689],{"x":685,"y":677,"fill":84,"style":678,"textAnchor":673},"fast LCP",[64,691],{"x":692,"y":94,"width":667,"height":415,"rx":97,"fill":132,"stroke":132,"style":133},"325.6",[87,694,696],{"x":695,"y":671,"fill":84,"style":672,"textAnchor":673},"380.0","Hydrate core",[87,698,699],{"x":695,"y":677,"fill":84,"style":678,"textAnchor":673},"reuse DOM",[64,701],{"x":702,"y":94,"width":667,"height":415,"rx":97,"fill":132,"stroke":132,"style":133},"474.4",[87,704,706],{"x":705,"y":671,"fill":84,"style":672,"textAnchor":673},"528.8","Replay events",[87,708,709],{"x":705,"y":677,"fill":84,"style":678,"textAnchor":673},"early clicks",[64,711],{"x":712,"y":94,"width":667,"height":415,"rx":97,"fill":98,"stroke":99,"style":100},"623.2",[87,714,716],{"x":715,"y":671,"fill":84,"style":672,"textAnchor":673},"677.6","Hydrate blocks",[87,718,719],{"x":715,"y":677,"fill":84,"style":678,"textAnchor":673},"on",[87,721,723],{"x":715,"y":722,"fill":84,"style":678,"textAnchor":673},"112.0","viewport\u002Finteracti",[87,725,719],{"x":715,"y":726,"fill":84,"style":678,"textAnchor":673},"128.0",[252,728],{"x1":729,"y1":730,"x2":731,"y2":730,"stroke":84,"strokeWidth":732,"style":733,"markerEnd":734},"136.8","99.5","174.8","1.5","stroke-opacity:0.6","url(#fa8199c3f3)",[252,736],{"x1":737,"y1":730,"x2":738,"y2":730,"stroke":84,"strokeWidth":732,"style":733,"markerEnd":734},"285.6","323.6",[252,740],{"x1":741,"y1":730,"x2":742,"y2":730,"stroke":84,"strokeWidth":732,"style":733,"markerEnd":734},"434.4","472.4",[252,744],{"x1":745,"y1":730,"x2":746,"y2":730,"stroke":84,"strokeWidth":732,"style":733,"markerEnd":734},"583.2","621.2",[155,748,750],{"id":749},"images-and-third-party-scripts","Images and Third-Party Scripts",[15,752,753,755,756,759,760,763,764,767],{},[25,754,238],{}," (the ",[25,757,758],{},"ngSrc"," directive) enforces dimensions, generates ",[25,761,762],{},"srcset","s with image CDN loaders, and supports a ",[25,765,766],{},"priority"," attribute that preloads the LCP image with high fetch priority — and warns in development when the LCP image lacks it. Third-party scripts should load after the app is stable; when using zone.js, initialise them outside the Angular zone so their timers and listeners do not trigger change detection. With zoneless, this concern disappears, but scripts still compete for the main thread during load.",[155,769,771],{"id":770},"template-performance-patterns","Template Performance Patterns",[15,773,774,775,778,779,781,782,784,785,787,788,791,792,795],{},"Many Angular slowdowns come from templates that do more work than they appear to. Method calls in bindings (",[25,776,777],{},"{{ formatPrice(item) }}",") run on every change detection cycle for every item; with default change detection and a busy page, that can mean thousands of calls per second. Replace them with pure pipes (cached per input) or ",[25,780,469],{}," signals. In ",[25,783,492],{}," loops, always provide a ",[25,786,497],{}," expression with a stable identity, so Angular reuses DOM nodes when the list changes instead of recreating them. Avoid deeply nested structural directives that create and destroy large subtrees on toggles; ",[25,789,790],{},"@if"," with lightweight placeholders or ",[25,793,794],{},"[hidden]"," for frequently toggled content can be cheaper. For long lists, the CDK virtual scroll renders only visible rows.",[15,797,798],{},"Pipes deserve a note of their own. Pure pipes (the default) only re-run when their input reference changes, which makes them a good place for formatting and filtering. Impure pipes run on every cycle and should be avoided for anything expensive.",[155,800,802],{"id":801},"rxjs-signals-and-subscriptions","RxJS, Signals and Subscriptions",[15,804,805,806,809,810,813,814,817],{},"Angular apps often use RxJS for HTTP and event streams. Subscriptions that update component properties in ",[25,807,808],{},"subscribe"," callbacks rely on zone.js to refresh the view, and leaking subscriptions keep work running after components are destroyed. Prefer the ",[25,811,812],{},"async"," pipe or ",[25,815,816],{},"toSignal"," to bridge observables into templates — both handle subscription lifecycle and notify Angular of changes, which also prepares the code for zoneless. For high-frequency streams (websocket prices, mouse positions), throttle or sample them to the display rate, and update only the signals that drive visible values.",[155,819,821],{"id":820},"measuring-angular-apps-in-the-field","Measuring Angular Apps in the Field",[15,823,824,825,828],{},"Report Core Web Vitals per route with the ",[25,826,827],{},"web-vitals"," library, using the Angular router's active route configuration rather than raw URLs. Angular DevTools is invaluable in development, but field data decides priorities: it shows which routes have poor LCP (often client-rendered or image-heavy routes) and which interactions have poor INP (often large forms and dashboards). Long Animation Frames attribution helps separate framework work (change detection, rendering) from application logic and third-party scripts in slow interactions. Pair field data with lab traces of the same routes on a throttled profile, so each regression or improvement can be explained by a concrete change in the trace rather than guessed at.",[155,830,832],{"id":831},"upgrading-older-angular-applications","Upgrading Older Angular Applications",[15,834,835,836,489,838,840,841,843,844,846,847,849],{},"Many Angular applications in production were built on older versions with NgModules, default change detection and client-side rendering. The upgrade path to modern performance features is incremental. First, keep Angular up to date — each major version improves the build pipeline and runtime, and the CLI's migrations automate much of the work (including converting to standalone components and the new control flow syntax). Next, adopt standalone components and the new ",[25,837,790],{},[25,839,492],{}," control flow, which are prerequisites for ",[25,842,27],{},". Then introduce signals in the components where interaction performance matters most, set them to ",[25,845,39],{},", and add ",[25,848,27],{}," blocks for heavy below-the-fold components. SSR with hydration and, finally, zoneless change detection complete the picture. Each step is valuable on its own, so teams can stop at any point with real gains. Measure after each step with the same throttled traces and field segments, so the team can see which changes moved LCP and INP and decide where to invest next.",[155,851,853],{"id":852},"a-rollout-plan","A Rollout Plan",[855,856,857,860,863,869,875,878,886],"ol",{},[186,858,859],{},"Profile change detection and bundles; rank routes by traffic and metrics.",[186,861,862],{},"Enable SSR with hydration and event replay for public routes.",[186,864,865,866,868],{},"Lazy-load routes and defer heavy components with ",[25,867,27],{},".",[186,870,871,872,874],{},"Convert hot components to signals and ",[25,873,39],{},"; remove functions from templates.",[186,876,877],{},"Move to zoneless once state changes are signal-based or explicitly notified.",[186,879,880,881,883,884,239],{},"Configure ",[25,882,238],{}," with ",[25,885,766],{},[186,887,888],{},"Add build budgets and field monitoring per route.",[155,890,892],{"id":891},"common-pitfalls","Common Pitfalls",[183,894,895,901,907,913,919],{},[186,896,897,900],{},[33,898,899],{},"Default change detection on large trees."," Every async event checks everything.",[186,902,903,906],{},[33,904,905],{},"Methods called in templates."," Recomputed on every cycle.",[186,908,909,912],{},[33,910,911],{},"Eagerly loaded features."," Large initial bundles.",[186,914,915,918],{},[33,916,917],{},"Client-side rendering for public pages."," Slow LCP.",[186,920,921,924],{},[33,922,923],{},"Third-party scripts inside the zone."," Trigger change detection constantly.",[155,926,928],{"id":927},"faq","FAQ",[930,931,934,938],"details",{"className":932},[933],"faq-item",[935,936,937],"summary",{},"What is zoneless change detection?",[15,939,940],{},"A mode where Angular runs without zone.js and schedules change detection only when signals change, events bound in templates fire, or code explicitly marks views for checking.",[930,942,944,947],{"className":943},[933],[935,945,946],{},"Should I use OnPush?",[15,948,949],{},"Yes, for most components. It limits checks to when inputs change or events occur inside the component, and prepares code for zoneless.",[930,951,953,956],{"className":952},[933],[935,954,955],{},"What are @defer blocks?",[15,957,958],{},"Template blocks that lazy-load a component's code and render it on a trigger (viewport, interaction, idle, timer), with placeholder and loading states.",[930,960,962,965],{"className":961},[933],[935,963,964],{},"Does Angular support SSR?",[15,966,967],{},"Yes. Angular SSR renders on the server, with non-destructive hydration, event replay and incremental hydration in recent versions.",[930,969,971,974],{"className":970},[933],[935,972,973],{},"Do signals replace RxJS?",[15,975,976],{},"Signals handle synchronous state and derived values; RxJS remains useful for complex async streams. Interop functions convert between them.",[930,978,980,983],{"className":979},[933],[935,981,982],{},"How do I find slow components?",[15,984,985],{},"Use the Angular DevTools profiler to record change detection and see time per component, then confirm with the browser Performance panel.",[930,987,989,992],{"className":988},[933],[935,990,991],{},"Is Angular slower than React or Vue?",[15,993,994],{},"Modern Angular with signals, zoneless and deferrable views performs comparably. Older patterns (default change detection, eager modules) are the usual cause of slowness.",[930,996,998,1001],{"className":997},[933],[935,999,1000],{},"Do standalone components improve performance?",[15,1002,1003,1004,1006],{},"They simplify lazy loading and ",[25,1005,27],{},", because dependencies are declared per component, which makes it easier to keep code out of the initial bundle.",[930,1008,1010,1013],{"className":1009},[933],[935,1011,1012],{},"What is NgOptimizedImage?",[15,1014,1015,1016,1018,1019,868],{},"A directive (",[25,1017,758],{},") that enforces image dimensions, generates responsive sources with image CDN loaders, and preloads the LCP image when marked with ",[25,1020,766],{},[930,1022,1024,1027],{"className":1023},[933],[935,1025,1026],{},"Should I use the CDK virtual scroll?",[15,1028,1029],{},"Yes, for long lists. It renders only visible items, keeping DOM size and change detection cost constant as lists grow.",[930,1031,1033,1036],{"className":1032},[933],[935,1034,1035],{},"How do I keep Angular bundles in check?",[15,1037,1038,1039,1041],{},"Set budgets in ",[25,1040,524],{}," so builds warn or fail when bundles grow, and review the build stats when they do.",[930,1043,1045,1048],{"className":1044},[933],[935,1046,1047],{},"Do signals work with OnPush?",[15,1049,1050],{},"Yes. Signals read in an OnPush component’s template mark it for checking when they change, which is the recommended combination before going zoneless.",[930,1052,1054,1057],{"className":1053},[933],[935,1055,1056],{},"Is server-side rendering expensive for Angular apps?",[15,1058,1059],{},"Rendering costs server CPU for every uncached request. Prerender static routes and cache shared pages at the CDN so the server only renders what must be dynamic.",[930,1061,1063,1066],{"className":1062},[933],[935,1064,1065],{},"Can Angular apps use speculation rules?",[15,1067,1068],{},"Yes, for full page navigations between server-rendered or prerendered routes. Client-side navigations use the router’s preloading strategies instead.",[155,1070,1072],{"id":1071},"guides-in-this-topic","Guides in This Topic",[183,1074,1075,1082,1089],{},[186,1076,1077,1081],{},[19,1078,1080],{"href":1079},"\u002Fframework-performance\u002Fangular-performance\u002Fgoing-zoneless-in-angular\u002F","Going zoneless in Angular"," — targeted change detection without zone.js.",[186,1083,1084,1088],{},[19,1085,1087],{"href":1086},"\u002Fframework-performance\u002Fangular-performance\u002Fdeferring-components-with-angular-defer-blocks\u002F","Deferring components with Angular @defer blocks"," — smaller initial bundles.",[186,1090,1091,1095],{},[19,1092,1094],{"href":1093},"\u002Fframework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay\u002F","Angular SSR hydration and event replay"," — fast first paint without lost clicks.",[155,1097,1099],{"id":1098},"related","Related",[183,1101,1102,1108,1115],{},[186,1103,1104,1107],{},[19,1105,1106],{"href":21},"Framework performance"," — the section overview.",[186,1109,1110,1114],{},[19,1111,1113],{"href":1112},"\u002Fcore-web-vitals-measurement\u002Fprofiling-event-handlers-for-inp\u002F","Profiling event handlers for INP"," — slow interactions in any framework.",[186,1116,1117,1121],{},[19,1118,1120],{"href":1119},"\u002Fjavascript-bundle-optimization-code-splitting\u002F","JavaScript bundle optimization"," — general bundle techniques.",[1123,1124,1126],"script",{"type":1125},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Angular Performance: Targeted Updates and Less Upfront Work\",\n  \"description\": \"How to make Angular applications fast — change detection, deferrable views, server rendering and hydration, and bundle size — using modern Angular features.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Environment Setup: Profile Change Detection\",\n      \"text\": \"Angular DevTools' profiler records change detection cycles, showing which components were checked and how long each took.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Capture a Baseline\",\n      \"text\": \"Record initial bundle size, LCP and INP per route in the field, and change detection time for key interactions in the lab with CPU throttling.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Isolate Causes\",\n      \"text\": \"Frequent change detection cycles → zone.js triggering on events that do not change state (scroll handlers, polling, third-party timers).\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Apply Modern Angular Patterns\",\n      \"text\": \"Adopt signals for component state and OnPush (or zoneless) change detection.\"\n    }\n  ]\n}\n",[1123,1128,1129],{"type":1125},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Angular Performance: Targeted Updates and Less Upfront Work\",\n  \"description\": \"How to make Angular applications fast — change detection, deferrable views, server rendering and hydration, and bundle size — using modern Angular features.\",\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\u002F\"\n  }\n}\n",[1123,1131,1132],{"type":1125},"\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}\n",[1134,1135,1136],"style",{},"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 .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}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}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);}",{"title":246,"searchDepth":260,"depth":260,"links":1138},[1139,1140,1141,1142,1143,1144,1145,1146,1147,1148,1149,1150,1151,1152,1153,1154,1155,1156,1157,1158,1159,1160],{"id":157,"depth":260,"text":158},{"id":180,"depth":260,"text":181},{"id":201,"depth":260,"text":202},{"id":208,"depth":260,"text":209},{"id":215,"depth":260,"text":216},{"id":225,"depth":260,"text":226},{"id":435,"depth":260,"text":436},{"id":452,"depth":260,"text":453},{"id":517,"depth":260,"text":518},{"id":583,"depth":260,"text":584},{"id":596,"depth":260,"text":597},{"id":613,"depth":260,"text":614},{"id":749,"depth":260,"text":750},{"id":770,"depth":260,"text":771},{"id":801,"depth":260,"text":802},{"id":820,"depth":260,"text":821},{"id":831,"depth":260,"text":832},{"id":852,"depth":260,"text":853},{"id":891,"depth":260,"text":892},{"id":927,"depth":260,"text":928},{"id":1071,"depth":260,"text":1072},{"id":1098,"depth":260,"text":1099},"How to make Angular applications fast — change detection, deferrable views, server rendering and hydration, and bundle size — using modern Angular features.","md",{"slug":1164,"type":1165,"breadcrumb":1166,"datePublished":1172,"dateModified":1172},"angular-performance","topic",[1167,1169,1170],{"name":1168,"url":489},"Home",{"name":22,"url":21},{"name":5,"url":1171},"\u002Fframework-performance\u002Fangular-performance\u002F","2026-10-06",true,"\u002Fframework-performance\u002Fangular-performance",{"title":1176,"description":1177},"Angular Performance: Zoneless, @defer and Hydration","Speed up Angular apps: signals and zoneless change detection, @defer blocks for lazy components, SSR with hydration and event replay, and lean bundles.","framework-performance\u002Fangular-performance\u002Findex","axtK4o5KFXdG9xnA1WRwxSiL8u2HSVl0omPBVcvEaf4",[1181,1184],{"title":22,"path":1182,"stem":1183,"children":-1},"\u002Fframework-performance","framework-performance\u002Findex",{"title":1185,"path":1186,"stem":1187,"children":-1},"Angular SSR Hydration and Event Replay","\u002Fframework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay","framework-performance\u002Fangular-performance\u002Fangular-ssr-hydration-and-event-replay\u002Findex",1791308070283]