[{"data":1,"prerenderedAt":1286},["ShallowReactive",2],{"content:\u002Fframework-performance\u002Freact-rendering-performance":3,"surroundings:\u002Fframework-performance\u002Freact-rendering-performance":1278},{"id":4,"title":5,"body":6,"description":1258,"extension":1259,"meta":1260,"navigation":1271,"path":1272,"seo":1273,"stem":1276,"__hash__":1277},"content\u002Fframework-performance\u002Freact-rendering-performance\u002Findex.md","React Rendering Performance",{"type":7,"value":8,"toc":1236},"minimark",[9,14,24,32,54,205,210,221,224,228,241,245,248,252,255,259,269,446,450,459,598,688,692,704,715,727,731,737,743,752,758,764,768,771,823,827,833,837,840,844,851,960,964,975,982,986,993,997,1004,1008,1029,1033,1065,1069,1081,1090,1099,1108,1117,1126,1135,1144,1153,1157,1194,1198,1221,1226,1229,1232],[10,11,13],"h1",{"id":12},"react-rendering-performance-making-updates-cheap","React Rendering Performance: Making Updates Cheap",[15,16,17,18,23],"p",{},"This topic is part of ",[19,20,22],"a",{"href":21},"\u002Fframework-performance\u002F","Framework Performance",". React's model is simple: when state changes, the component that owns the state re-renders, and by default so do all of its descendants. React then compares the new element tree with the previous one and applies the minimal DOM changes. This is fast for small trees and occasional updates. It becomes slow when a state change near the top of the tree re-renders hundreds or thousands of components, when components do expensive work during render, or when updates happen on every keystroke.",[15,25,26,27,31],{},"These costs land directly on ",[28,29,30],"strong",{},"Interaction to Next Paint",". A click handler that sets state triggers a render and commit before the browser can paint; if that render takes 150ms on a mid-range phone, INP for that interaction is at least 150ms. Hydration — React rendering the whole tree once on the client to attach to server HTML — adds to Total Blocking Time and to the INP of interactions that happen during load.",[15,33,34,35,39,40,39,43,46,47,39,50,53],{},"React provides tools at several levels: profiling (React DevTools Profiler), memoization (",[36,37,38],"code",{},"React.memo",", ",[36,41,42],{},"useMemo",[36,44,45],{},"useCallback","), automatic memoization (the React Compiler), state design (colocating state, splitting context), and concurrent rendering (",[36,48,49],{},"useTransition",[36,51,52],{},"useDeferredValue",") that keeps urgent updates responsive while expensive ones render in the background.",[15,55,56],{},[57,58,64,65,64,72,64,76,64,79,64,97,64,105,64,111,64,119,64,126,64,131,64,137,64,141,64,144,64,148,64,151,64,155,64,158,64,161,64,165,64,168,64,171,64,174,64,178,64,181,64,184,64,193,64,197,64,201,64],"svg",{"viewBox":59,"width":60,"role":61,"ariaLabel":62,"style":63},"0 0 760 149","100%","img","Steps from a state update through rendering, reconciliation and commit to the browser painting.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[66,67],"rect",{"className":68,"x":70,"y":70,"width":60,"height":60,"fill":71},[69],"svg-canvas","0","#ffffff",[73,74,75],"title",{},"What happens on a React state update",[77,78,62],"desc",{},[80,81,82],"defs",{},[83,84,91],"marker",{"id":85,"viewBox":86,"refX":87,"refY":88,"markerWidth":89,"markerHeight":89,"orient":90},"fa8228b2f8","0 0 10 10","9","5","7","auto-start-reverse",[92,93],"path",{"d":94,"fill":95,"style":96},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[66,98],{"x":99,"y":99,"width":100,"height":101,"rx":102,"fill":103,"stroke":95,"style":104},"1","758","147","10","none","stroke-opacity:0.18",[106,107,75],"text",{"x":108,"y":109,"fill":95,"style":110},"28.0","34.0","font-size:16px;font-weight:700",[66,112],{"x":108,"y":113,"width":114,"height":115,"rx":116,"fill":117,"stroke":117,"style":118},"56.0","108.8","71.0","6","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[106,120,125],{"x":121,"y":122,"fill":95,"style":123,"textAnchor":124},"82.4","78.0","font-size:13px;font-weight:700","middle","setState",[106,127,130],{"x":121,"y":128,"fill":95,"style":129,"textAnchor":124},"96.0","font-size:12px","in event handler",[66,132],{"x":133,"y":113,"width":114,"height":115,"rx":116,"fill":134,"stroke":135,"style":136},"176.8","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[106,138,140],{"x":139,"y":122,"fill":95,"style":123,"textAnchor":124},"231.2","Render",[106,142,143],{"x":139,"y":128,"fill":95,"style":129,"textAnchor":124},"component +",[106,145,147],{"x":139,"y":146,"fill":95,"style":129,"textAnchor":124},"112.0","descendants",[66,149],{"x":150,"y":113,"width":114,"height":115,"rx":116,"fill":117,"stroke":117,"style":118},"325.6",[106,152,154],{"x":153,"y":122,"fill":95,"style":123,"textAnchor":124},"380.0","Reconcile",[106,156,157],{"x":153,"y":128,"fill":95,"style":129,"textAnchor":124},"diff element trees",[66,159],{"x":160,"y":113,"width":114,"height":115,"rx":116,"fill":117,"stroke":117,"style":118},"474.4",[106,162,164],{"x":163,"y":122,"fill":95,"style":123,"textAnchor":124},"528.8","Commit",[106,166,167],{"x":163,"y":128,"fill":95,"style":129,"textAnchor":124},"DOM changes +",[106,169,170],{"x":163,"y":146,"fill":95,"style":129,"textAnchor":124},"effects",[66,172],{"x":173,"y":113,"width":114,"height":115,"rx":116,"fill":117,"stroke":117,"style":118},"623.2",[106,175,177],{"x":176,"y":122,"fill":95,"style":123,"textAnchor":124},"677.6","Browser",[106,179,180],{"x":176,"y":128,"fill":95,"style":129,"textAnchor":124},"style, layout,",[106,182,183],{"x":176,"y":146,"fill":95,"style":129,"textAnchor":124},"paint",[185,186],"line",{"x1":187,"y1":188,"x2":189,"y2":188,"stroke":95,"strokeWidth":190,"style":191,"markerEnd":192},"136.8","91.5","174.8","1.5","stroke-opacity:0.6","url(#fa8228b2f8)",[185,194],{"x1":195,"y1":188,"x2":196,"y2":188,"stroke":95,"strokeWidth":190,"style":191,"markerEnd":192},"285.6","323.6",[185,198],{"x1":199,"y1":188,"x2":200,"y2":188,"stroke":95,"strokeWidth":190,"style":191,"markerEnd":192},"434.4","472.4",[185,202],{"x1":203,"y1":188,"x2":204,"y2":188,"stroke":95,"strokeWidth":190,"style":191,"markerEnd":192},"583.2","621.2",[206,207,209],"h2",{"id":208},"the-metric-degradation-this-topic-addresses","The Metric Degradation This Topic Addresses",[15,211,212,213,216,217,220],{},"Slow React rendering shows up as poor ",[28,214,215],{},"INP",", particularly on interactions that update shared state (filters, selections, form inputs, toggles), and as long tasks during ",[28,218,219],{},"hydration"," that delay early interactions. In INP attribution, the time appears as processing duration (event handler plus synchronous render) and presentation delay (commit and the browser's rendering work). With the Long Animation Frames API, script attribution points at React's internals and your components.",[15,222,223],{},"Typical symptoms: typing in an input lags behind keystrokes; selecting a row in a table takes hundreds of milliseconds; opening a menu re-renders the whole page; hydration produces a single long task of 300ms or more on mobile.",[206,225,227],{"id":226},"prerequisites","Prerequisites",[229,230,231,235,238],"ul",{},[232,233,234],"li",{},"React DevTools with the Profiler, and a development or profiling build for detailed component timings.",[232,236,237],{},"Field INP data with interaction targets, to know which interactions to fix.",[232,239,240],{},"A Performance panel trace of the slow interactions with CPU throttling.",[206,242,244],{"id":243},"_1-environment-setup-profile-the-slow-interaction","1. Environment Setup: Profile the Slow Interaction",[15,246,247],{},"Open React DevTools → Profiler, enable \"Record why each component rendered while profiling\" in settings, start recording, perform the interaction, and stop. The flame graph shows each commit; components are coloured by render time, and grey components did not render. The ranked view lists the most expensive components.",[206,249,251],{"id":250},"_2-capture-a-baseline","2. Capture a Baseline",[15,253,254],{},"Record the commit duration for the interaction and the number of components that rendered. In the Performance panel (with throttling), record the total interaction time. In the field, note INP for the interaction target.",[206,256,258],{"id":257},"_3-isolate-wasted-and-expensive-renders","3. Isolate Wasted and Expensive Renders",[15,260,261,264,265,268],{},[28,262,263],{},"Wasted renders"," are components that re-rendered but produced the same output — usually because a parent re-rendered or a context value changed identity. ",[28,266,267],{},"Expensive renders"," are components whose render function itself is slow: large lists, heavy computations, complex charts. \"Why did this render?\" in the Profiler distinguishes props changes, state changes, context changes and parent renders.",[270,271,276],"pre",{"className":272,"code":273,"language":274,"meta":275,"style":275},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F Common cause: a new object\u002Farray\u002Ffunction identity on every parent render.\nfunction Page({ products }) {\n  const [query, setQuery] = useState('');\n  const options = { currency: 'EUR' };                     \u002F\u002F new object every render\n  return \u003CProductGrid products={products} options={options} onSelect={(id) => track(id)} \u002F>;\n}\n\u002F\u002F trade-off: inline objects and callbacks are fine for cheap children; they only\n\u002F\u002F matter when the child is memoized or expensive, where they defeat memoization.\n","javascript","",[36,277,278,286,308,345,368,428,434,440],{"__ignoreMap":275},[279,280,282],"span",{"class":185,"line":281},1,[279,283,285],{"class":284},"sjfSM","\u002F\u002F Common cause: a new object\u002Farray\u002Ffunction identity on every parent render.\n",[279,287,289,293,297,301,305],{"class":185,"line":288},2,[279,290,292],{"class":291},"sPARh","function",[279,294,296],{"class":295},"smZ65"," Page",[279,298,300],{"class":299},"saISM","({ ",[279,302,304],{"class":303},"sQw3B","products",[279,306,307],{"class":299}," }) {\n",[279,309,311,314,317,321,323,326,329,332,335,338,342],{"class":185,"line":310},3,[279,312,313],{"class":291},"  const",[279,315,316],{"class":299}," [",[279,318,320],{"class":319},"sPXB4","query",[279,322,39],{"class":299},[279,324,325],{"class":319},"setQuery",[279,327,328],{"class":299},"] ",[279,330,331],{"class":291},"=",[279,333,334],{"class":295}," useState",[279,336,337],{"class":299},"(",[279,339,341],{"class":340},"sZ8jY","''",[279,343,344],{"class":299},");\n",[279,346,348,350,353,356,359,362,365],{"class":185,"line":347},4,[279,349,313],{"class":291},[279,351,352],{"class":319}," options",[279,354,355],{"class":291}," =",[279,357,358],{"class":299}," { currency: ",[279,360,361],{"class":340},"'EUR'",[279,363,364],{"class":299}," };                     ",[279,366,367],{"class":284},"\u002F\u002F new object every render\n",[279,369,371,374,377,381,384,387,389,392,394,396,399,401,404,406,408,411,414,417,420,423,425],{"class":185,"line":370},5,[279,372,373],{"class":291},"  return",[279,375,376],{"class":299}," \u003C",[279,378,380],{"class":379},"sZBmE","ProductGrid",[279,382,383],{"class":319}," products",[279,385,386],{"class":291},"={",[279,388,304],{"class":299},[279,390,391],{"class":291},"}",[279,393,352],{"class":319},[279,395,386],{"class":291},[279,397,398],{"class":299},"options",[279,400,391],{"class":291},[279,402,403],{"class":319}," onSelect",[279,405,386],{"class":291},[279,407,337],{"class":299},[279,409,410],{"class":303},"id",[279,412,413],{"class":299},") ",[279,415,416],{"class":291},"=>",[279,418,419],{"class":295}," track",[279,421,422],{"class":299},"(id)",[279,424,391],{"class":291},[279,426,427],{"class":299}," \u002F>;\n",[279,429,431],{"class":185,"line":430},6,[279,432,433],{"class":299},"}\n",[279,435,437],{"class":185,"line":436},7,[279,438,439],{"class":284},"\u002F\u002F trade-off: inline objects and callbacks are fine for cheap children; they only\n",[279,441,443],{"class":185,"line":442},8,[279,444,445],{"class":284},"\u002F\u002F matter when the child is memoized or expensive, where they defeat memoization.\n",[206,447,449],{"id":448},"_4-apply-render-less-render-cheaper-render-later","4. Apply: Render Less, Render Cheaper, Render Later",[15,451,452,453,455,456,458],{},"Render less by colocating state (move it down to where it is used), splitting context, and memoizing expensive subtrees (or adopting the React Compiler). Render cheaper by virtualizing long lists and moving expensive calculations out of render. Render later by marking non-urgent updates with ",[36,454,49],{}," or ",[36,457,52],{},".",[270,460,462],{"className":272,"code":461,"language":274,"meta":275,"style":275},"\u002F\u002F Colocate state: the input's state lives in the input component, not the page.\nfunction SearchBox({ onSearch }) {\n  const [value, setValue] = useState('');\n  return \u003Cinput value={value} onChange={(e) => setValue(e.target.value)} onKeyDown={(e) => e.key === 'Enter' && onSearch(value)} \u002F>;\n}\n\u002F\u002F trade-off: the page only learns the query on Enter; for live search, combine\n\u002F\u002F with useDeferredValue so results update without blocking typing.\n",[36,463,464,469,483,509,584,588,593],{"__ignoreMap":275},[279,465,466],{"class":185,"line":281},[279,467,468],{"class":284},"\u002F\u002F Colocate state: the input's state lives in the input component, not the page.\n",[279,470,471,473,476,478,481],{"class":185,"line":288},[279,472,292],{"class":291},[279,474,475],{"class":295}," SearchBox",[279,477,300],{"class":299},[279,479,480],{"class":303},"onSearch",[279,482,307],{"class":299},[279,484,485,487,489,492,494,497,499,501,503,505,507],{"class":185,"line":310},[279,486,313],{"class":291},[279,488,316],{"class":299},[279,490,491],{"class":319},"value",[279,493,39],{"class":299},[279,495,496],{"class":319},"setValue",[279,498,328],{"class":299},[279,500,331],{"class":291},[279,502,334],{"class":295},[279,504,337],{"class":299},[279,506,341],{"class":340},[279,508,344],{"class":299},[279,510,511,513,515,518,521,523,525,527,530,532,534,537,539,541,544,547,549,552,554,556,558,560,562,565,568,571,574,577,580,582],{"class":185,"line":347},[279,512,373],{"class":291},[279,514,376],{"class":299},[279,516,517],{"class":379},"input",[279,519,520],{"class":319}," value",[279,522,386],{"class":291},[279,524,491],{"class":299},[279,526,391],{"class":291},[279,528,529],{"class":319}," onChange",[279,531,386],{"class":291},[279,533,337],{"class":299},[279,535,536],{"class":303},"e",[279,538,413],{"class":299},[279,540,416],{"class":291},[279,542,543],{"class":295}," setValue",[279,545,546],{"class":299},"(e.target.value)",[279,548,391],{"class":291},[279,550,551],{"class":319}," onKeyDown",[279,553,386],{"class":291},[279,555,337],{"class":299},[279,557,536],{"class":303},[279,559,413],{"class":299},[279,561,416],{"class":291},[279,563,564],{"class":299}," e.key ",[279,566,567],{"class":291},"===",[279,569,570],{"class":340}," 'Enter'",[279,572,573],{"class":291}," &&",[279,575,576],{"class":295}," onSearch",[279,578,579],{"class":299},"(value)",[279,581,391],{"class":291},[279,583,427],{"class":299},[279,585,586],{"class":185,"line":370},[279,587,433],{"class":299},[279,589,590],{"class":185,"line":430},[279,591,592],{"class":284},"\u002F\u002F trade-off: the page only learns the query on Enter; for live search, combine\n",[279,594,595],{"class":185,"line":436},[279,596,597],{"class":284},"\u002F\u002F with useDeferredValue so results update without blocking typing.\n",[15,599,600],{},[57,601,64,604,64,607,64,610,64,612,64,615,64,617,64,625,64,632,64,639,64,644,64,648,64,653,64,657,64,661,64,666,64,670,64,674,64,678,64,682,64],{"viewBox":602,"width":60,"role":61,"ariaLabel":603,"style":63},"0 0 760 216","Bar chart of React commit duration for a filter change before and after successive rendering optimizations.",[66,605],{"className":606,"x":70,"y":70,"width":60,"height":60,"fill":71},[69],[73,608,609],{},"Commit time for a filter change on a 500-row table",[77,611,603],{},[66,613],{"x":99,"y":99,"width":100,"height":614,"rx":102,"fill":103,"stroke":95,"style":104},"214",[106,616,609],{"x":108,"y":109,"fill":95,"style":110},[185,618],{"x1":619,"y1":620,"x2":619,"y2":621,"stroke":135,"strokeWidth":190,"strokeDashArray":622,"style":624},"412.0","72.0","194.0",[623,623],"4","stroke-opacity:0.9",[106,626,631],{"x":627,"y":628,"fill":95,"style":629,"textAnchor":630},"217.1","92.0","font-size:13px","end","Baseline (whole page re-renders)",[66,633],{"x":634,"y":122,"width":635,"height":636,"rx":637,"fill":103,"stroke":95,"style":638},"229.1","438.9","19","3","fill-opacity:0.7;stroke-opacity:0.35",[106,640,643],{"x":641,"y":628,"fill":95,"style":642},"674.0","font-size:12px;font-weight:600","240ms",[106,645,647],{"x":627,"y":646,"fill":95,"style":629,"textAnchor":630},"123.0","State colocated",[66,649],{"x":634,"y":650,"width":651,"height":636,"rx":637,"fill":134,"stroke":135,"style":652},"109.0","274.3","fill-opacity:0.7;stroke-opacity:0.9",[106,654,656],{"x":655,"y":646,"fill":95,"style":642},"509.4","150ms",[106,658,660],{"x":627,"y":659,"fill":95,"style":629,"textAnchor":630},"154.0","+ memoized rows",[66,662],{"x":634,"y":663,"width":664,"height":636,"rx":637,"fill":117,"stroke":117,"style":665},"140.0","128.0","fill-opacity:0.55;stroke-opacity:0.9",[106,667,669],{"x":668,"y":659,"fill":95,"style":642},"363.1","70ms",[106,671,673],{"x":627,"y":672,"fill":95,"style":629,"textAnchor":630},"185.0","+ virtualized table",[66,675],{"x":634,"y":676,"width":677,"height":636,"rx":637,"fill":134,"stroke":135,"style":652},"171.0","32.9",[106,679,681],{"x":680,"y":672,"fill":95,"style":642},"268.0","18ms",[106,683,687],{"x":619,"y":684,"fill":685,"style":686,"textAnchor":124},"68.0","#8a6608","font-size:12px;font-weight:700","budget",[206,689,691],{"id":690},"deconstructing-reacts-rendering-rules","Deconstructing React's Rendering Rules",[15,693,694,695,697,698,700,701,703],{},"A component re-renders when its state changes, when its parent re-renders (unless it is memoized and its props are shallow-equal), or when a context it consumes changes value. Memoization with ",[36,696,38],{}," skips re-rendering when props are equal by reference; it is defeated by new object, array or function props created on every render — hence ",[36,699,42],{}," and ",[36,702,45],{}," to stabilise them. Context consumers re-render whenever the context value changes identity, regardless of which part of the value they use.",[15,705,706,707,39,709,700,711,714],{},"The React Compiler applies memoization automatically at build time: it analyses components and hooks, and caches JSX, computed values and callbacks so that re-renders skip work when inputs have not changed. It removes most of the need for manual ",[36,708,42],{},[36,710,45],{},[36,712,713],{},"memo",", provided code follows the Rules of React (pure render, no mutation of props or state).",[15,716,717,718,722,723,726],{},"Concurrent rendering changes ",[719,720,721],"em",{},"when"," work happens rather than how much. Updates wrapped in ",[36,724,725],{},"startTransition"," are interruptible: React renders them in the background, yields to the browser between chunks, and abandons stale renders when newer input arrives. Urgent updates — the input value itself — render immediately.",[206,728,730],{"id":729},"advanced-diagnostics-and-edge-cases","Advanced Diagnostics and Edge Cases",[15,732,733,736],{},[28,734,735],{},"Hydration cost."," Large client component trees hydrate in one go unless split by Suspense boundaries. Server Components reduce what hydrates at all.",[15,738,739,742],{},[28,740,741],{},"State management libraries."," Global stores that notify all subscribers on every change can cause broad re-renders; use selectors that subscribe to slices.",[15,744,745,64,748,751],{},[28,746,747],{},"Effects that set state.",[36,749,750],{},"useEffect"," that sets state after render causes a second render; derive values during render instead where possible.",[15,753,754,757],{},[28,755,756],{},"Development mode."," React's development build and Strict Mode double-invoke renders; always measure with production builds.",[15,759,760,763],{},[28,761,762],{},"Lists without keys or with index keys."," Cause unnecessary DOM work and state bugs when items reorder.",[206,765,767],{"id":766},"validation-and-budgeting","Validation and Budgeting",[15,769,770],{},"After changes, re-profile: fewer components should render per interaction, commit durations should drop, and the Performance trace should show shorter tasks. In the field, INP for the target interactions should improve. Budget commit duration for key interactions (for example under 50ms on a 4x throttled profile) in lab tests.",[772,773,774,787],"table",{},[775,776,777],"thead",{},[778,779,780,784],"tr",{},[781,782,783],"th",{},"Check",[781,785,786],{},"Target",[788,789,790,799,807,815],"tbody",{},[778,791,792,796],{},[793,794,795],"td",{},"Commit duration for key interactions (4x CPU)",[793,797,798],{},"\u003C 50 ms",[778,800,801,804],{},[793,802,803],{},"Components rendered for a local update",[793,805,806],{},"Only the affected subtree",[778,808,809,812],{},[793,810,811],{},"Hydration long task (mobile profile)",[793,813,814],{},"\u003C 200 ms per task",[778,816,817,820],{},[793,818,819],{},"INP p75 for key interactions",[793,821,822],{},"\u003C 200 ms",[206,824,826],{"id":825},"worked-example-a-crm-contact-list","Worked Example: A CRM Contact List",[15,828,829,830,832],{},"A CRM's contact list page held filters, selection and the contact list in a single page component with a context providing the whole state object to all children. Typing in the search box re-rendered the page, 800 contact rows and the sidebar on every keystroke: 180ms per keystroke on a mid-range laptop with throttling, and INP p75 of 340ms. The team moved the search input's state into the input component, passed the query to the list through ",[36,831,52],{},", split the context into a selection context and a filters context, memoized the row component, and virtualized the list. Keystrokes now committed in 4ms; the list updated in the background in about 30ms. INP p75 dropped to 120ms.",[206,834,836],{"id":835},"when-memoization-helps-and-when-it-does-not","When Memoization Helps — and When It Does Not",[15,838,839],{},"Memoization has a cost: comparing props and storing results on every render. For cheap components, that cost can exceed the render it skips. It pays off for components that are expensive to render, render often with unchanged props, or sit at the top of large subtrees. A practical rule: measure first, memoize the few components that show up at the top of the Profiler's ranked view, and stabilise the props they receive. Or adopt the React Compiler and let it handle the common cases consistently.",[206,841,843],{"id":842},"concurrent-features-for-responsiveness","Concurrent Features for Responsiveness",[15,845,846,700,848,850],{},[36,847,49],{},[36,849,52],{}," do not make rendering faster; they make it interruptible and lower priority, so the browser can paint urgent updates (and respond to input) while expensive rendering continues. They are ideal for search-as-you-type, filter changes on large lists, tab switches that render heavy content, and route transitions. They cannot help with a single synchronous computation that blocks the thread — that still needs to be made cheaper, chunked, or moved to a worker.",[15,852,853],{},[57,854,64,857,64,860,64,863,64,865,64,868,64,870,64,875,64,879,64,885,64,891,64,896,64,899,64,903,64,906,64,910,64,913,64,916,64,920,64,924,64,929,64,935,64,939,64,943,64,946,64,949,64,953,64,957,64],{"viewBox":855,"width":60,"role":61,"ariaLabel":856,"style":63},"0 0 760 200","Timeline showing a keystroke blocked by list rendering compared with an urgent input update and deferred list rendering.",[66,858],{"className":859,"x":70,"y":70,"width":60,"height":60,"fill":71},[69],[73,861,862],{},"Keystroke handling with and without useDeferredValue",[77,864,856],{},[66,866],{"x":99,"y":99,"width":100,"height":867,"rx":102,"fill":103,"stroke":95,"style":104},"198",[106,869,862],{"x":108,"y":109,"fill":95,"style":110},[185,871],{"x1":872,"y1":620,"x2":872,"y2":873,"stroke":135,"strokeWidth":190,"strokeDashArray":874,"style":624},"535.4","156.0",[623,623],[106,876,878],{"x":108,"y":128,"fill":95,"style":877},"font-size:12.5px;font-weight:700","Without",[66,880],{"x":881,"y":122,"width":882,"height":883,"rx":637,"fill":103,"stroke":95,"style":884},"154.9","341.6","26.0","stroke-opacity:0.35",[106,886,890],{"x":887,"y":888,"fill":95,"style":889,"textAnchor":124},"325.7","95.5","font-size:11.5px","render input + 800 rows",[66,892],{"x":893,"y":122,"width":894,"height":883,"rx":637,"fill":95,"stroke":95,"style":895},"498.0","36.6","fill-opacity:0.06;stroke-opacity:0.4",[106,897,183],{"x":898,"y":888,"fill":95,"style":889,"textAnchor":124},"516.3",[106,900,902],{"x":108,"y":901,"fill":95,"style":877},"134.0","With deferred value",[66,904],{"x":881,"y":905,"width":894,"height":883,"rx":637,"fill":134,"stroke":135,"style":136},"116.0",[106,907,517],{"x":908,"y":909,"fill":95,"style":889,"textAnchor":124},"173.2","133.5",[66,911],{"x":912,"y":905,"width":894,"height":883,"rx":637,"fill":134,"stroke":135,"style":136},"193.0",[106,914,183],{"x":915,"y":909,"fill":95,"style":889,"textAnchor":124},"211.3",[66,917],{"x":918,"y":905,"width":919,"height":883,"rx":637,"fill":117,"stroke":117,"style":118},"231.1","151.0",[106,921,923],{"x":922,"y":909,"fill":95,"style":889,"textAnchor":124},"306.6","list renders in background",[185,925],{"x1":926,"y1":873,"x2":927,"y2":873,"stroke":95,"style":928},"154.1","726.0","stroke-opacity:0.4",[106,930,934],{"x":931,"y":932,"fill":933,"style":889,"textAnchor":124},"164.8","173.0","#51617a","0ms",[106,936,938],{"x":937,"y":932,"fill":933,"style":889,"textAnchor":124},"249.4","50ms",[106,940,942],{"x":941,"y":932,"fill":933,"style":889,"textAnchor":124},"344.7","100ms",[106,944,656],{"x":945,"y":932,"fill":933,"style":889,"textAnchor":124},"440.1",[106,947,948],{"x":872,"y":932,"fill":933,"style":889,"textAnchor":124},"200ms",[106,950,952],{"x":951,"y":932,"fill":933,"style":889,"textAnchor":124},"630.7","250ms",[106,954,956],{"x":955,"y":932,"fill":933,"style":889,"textAnchor":124},"715.3","300ms",[106,958,959],{"x":872,"y":684,"fill":685,"style":686,"textAnchor":124},"INP poor",[206,961,963],{"id":962},"hydration-and-server-components","Hydration and Server Components",[15,965,966,967,970,971,974],{},"Rendering performance starts before the first interaction. On a server-rendered React page, hydration renders every client component once in the browser, and that render runs while users may already be trying to tap and scroll. Two levers reduce it. ",[28,968,969],{},"Server Components"," (in frameworks that support them, such as the Next.js App Router) never hydrate: their output is HTML plus a serialised description, and their code is not sent to the client. Moving static parts of the page — content, layout, product descriptions — into server components and keeping client components small and leaf-level shrinks both the bundle and the hydration work. ",[28,972,973],{},"Suspense boundaries"," split hydration into independent chunks, and React prioritises hydrating the boundary a user interacts with first (selective hydration), so a tap on a button in one section does not wait for the whole page.",[15,976,977,978,981],{},"A useful audit is to list the client components on a key page and ask, for each, whether it needs state, effects or event handlers. Components that only render props often became client components by accident (a ",[36,979,980],{},"\"use client\""," directive at the top of a shared module pulls everything it imports into the client bundle).",[206,983,985],{"id":984},"expensive-work-outside-render","Expensive Work Outside Render",[15,987,988,989,992],{},"Not all slow interactions are rendering problems. Event handlers that process large data synchronously, effects that run expensive logic after commit, and third-party scripts that react to DOM changes all contribute to INP. The browser Performance panel shows the full picture: React's render and commit appear alongside your handler code, effects, and the browser's style, layout and paint. If the handler itself is slow, break the work into chunks with ",[36,990,991],{},"scheduler.yield()"," or move it to a Web Worker; if effects are slow, check whether the work can run during render (derived state) or be deferred. Fixing only React rendering when the handler is the bottleneck brings little improvement.",[206,994,996],{"id":995},"testing-rendering-performance-in-ci","Testing Rendering Performance in CI",[15,998,999,1000,1003],{},"Rendering regressions are easy to introduce — a new context, a prop that changes identity, an unmemoized list — and hard to spot in code review. Add a few automated checks for key interactions: a Playwright or Puppeteer script that performs the interaction on a profiling build and records render counts or commit durations via the ",[36,1001,1002],{},"\u003CProfiler>"," callback, failing if they exceed a budget. Even a simple assertion such as \"typing one character in search renders fewer than 50 components\" catches most cascades before they reach users.",[206,1005,1007],{"id":1006},"a-rollout-plan","A Rollout Plan",[1009,1010,1011,1014,1017,1020,1023,1026],"ol",{},[232,1012,1013],{},"Rank interactions by field INP and profile the worst in React DevTools.",[232,1015,1016],{},"Colocate state and split context to shrink what re-renders.",[232,1018,1019],{},"Memoize the expensive components the Profiler identifies, or adopt the React Compiler.",[232,1021,1022],{},"Virtualize long lists and move heavy computation out of render.",[232,1024,1025],{},"Wrap non-urgent updates in transitions or deferred values.",[232,1027,1028],{},"Re-measure in the lab and field; add commit-time checks for key interactions.",[206,1030,1032],{"id":1031},"common-pitfalls","Common Pitfalls",[229,1034,1035,1041,1047,1053,1059],{},[232,1036,1037,1040],{},[28,1038,1039],{},"Memoizing everything by hand."," Adds overhead and noise; target expensive components.",[232,1042,1043,1046],{},[28,1044,1045],{},"One context for all app state."," Every consumer re-renders on any change.",[232,1048,1049,1052],{},[28,1050,1051],{},"State too high in the tree."," Local UI state stored in page-level components.",[232,1054,1055,1058],{},[28,1056,1057],{},"Profiling development builds."," Misleading numbers due to development checks.",[232,1060,1061,1064],{},[28,1062,1063],{},"Expecting transitions to fix slow computations."," They defer work, not shrink it.",[206,1066,1068],{"id":1067},"faq","FAQ",[1070,1071,1074,1078],"details",{"className":1072},[1073],"faq-item",[1075,1076,1077],"summary",{},"What is a wasted render?",[15,1079,1080],{},"A render that produces the same output as before, usually because a parent re-rendered or a prop changed identity without changing value.",[1070,1082,1084,1087],{"className":1083},[1073],[1075,1085,1086],{},"Should I wrap every component in React.memo?",[15,1088,1089],{},"No. Memoize components that are expensive or render frequently with unchanged props. For others, memoization adds overhead without benefit.",[1070,1091,1093,1096],{"className":1092},[1073],[1075,1094,1095],{},"Does the React Compiler replace useMemo and useCallback?",[15,1097,1098],{},"For most cases, yes. It memoizes automatically at build time. Manual memoization may still be useful for specific cases the compiler cannot optimise.",[1070,1100,1102,1105],{"className":1101},[1073],[1075,1103,1104],{},"How do I find what caused a re-render?",[15,1106,1107],{},"Enable \"Record why each component rendered\" in React DevTools Profiler settings; each component's details show whether props, state, context or a parent caused it.",[1070,1109,1111,1114],{"className":1110},[1073],[1075,1112,1113],{},"Does useTransition make my app faster?",[15,1115,1116],{},"It makes urgent updates responsive by rendering non-urgent ones in the background and interruptibly. Total work is similar.",[1070,1118,1120,1123],{"className":1119},[1073],[1075,1121,1122],{},"Why is hydration slow?",[15,1124,1125],{},"React renders the full client component tree once to attach to server HTML. Large trees, expensive components and third-party scripts running at the same time make it slow.",[1070,1127,1129,1132],{"className":1128},[1073],[1075,1130,1131],{},"Do Server Components help INP?",[15,1133,1134],{},"Indirectly: less client code and less to hydrate means fewer long tasks during load. Interactions in client components still follow the same rendering rules.",[1070,1136,1138,1141],{"className":1137},[1073],[1075,1139,1140],{},"Is React slower than other frameworks?",[15,1142,1143],{},"React’s default re-render model does more work per update than fine-grained reactive frameworks, but well-structured React apps — with colocated state, memoization or the compiler, and virtualization — easily meet INP targets. Architecture matters more than the framework.",[1070,1145,1147,1150],{"className":1146},[1073],[1075,1148,1149],{},"Should I use a state management library for performance?",[15,1151,1152],{},"For shared state that changes often, a store with selectors limits re-renders to components that use the changed slice. For rarely changing values, context or props are fine.",[206,1154,1156],{"id":1155},"guides-in-this-topic","Guides in This Topic",[229,1158,1159,1166,1173,1180,1187],{},[232,1160,1161,1165],{},[19,1162,1164],{"href":1163},"\u002Fframework-performance\u002Freact-rendering-performance\u002Ffinding-wasted-renders-with-the-react-profiler\u002F","Finding wasted renders with the React Profiler"," — profiling workflow.",[232,1167,1168,1172],{},[19,1169,1171],{"href":1170},"\u002Fframework-performance\u002Freact-rendering-performance\u002Fwhen-usememo-and-usecallback-actually-help\u002F","When useMemo and useCallback actually help"," — targeted manual memoization.",[232,1174,1175,1179],{},[19,1176,1178],{"href":1177},"\u002Fframework-performance\u002Freact-rendering-performance\u002Fadopting-the-react-compiler\u002F","Adopting the React Compiler"," — automatic memoization in practice.",[232,1181,1182,1186],{},[19,1183,1185],{"href":1184},"\u002Fframework-performance\u002Freact-rendering-performance\u002Fstopping-context-re-render-cascades\u002F","Stopping context re-render cascades"," — context design for performance.",[232,1188,1189,1193],{},[19,1190,1192],{"href":1191},"\u002Fframework-performance\u002Freact-rendering-performance\u002Fusetransition-and-usedeferredvalue-for-inp\u002F","useTransition and useDeferredValue for INP"," — keeping urgent updates fast.",[206,1195,1197],{"id":1196},"related","Related",[229,1199,1200,1207,1214],{},[232,1201,1202,1206],{},[19,1203,1205],{"href":1204},"\u002Frendering-css-performance\u002Fdom-size-and-list-virtualization\u002Fvirtualizing-long-lists-in-react\u002F","Virtualizing long lists in React"," — rendering only visible rows.",[232,1208,1209,1213],{},[19,1210,1212],{"href":1211},"\u002Fcore-web-vitals-measurement\u002Fprofiling-event-handlers-for-inp\u002F","Profiling event handlers for INP"," — finding slow interactions.",[232,1215,1216,1220],{},[19,1217,1219],{"href":1218},"\u002Fframework-performance\u002Fnextjs-performance\u002F","Next.js performance"," — React in the Next.js framework.",[1222,1223,1225],"script",{"type":1224},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"React Rendering Performance: Making Updates Cheap\",\n  \"description\": \"How React decides what to re-render, how to find wasted and expensive renders, and the tools — memoization, the compiler, state design and concurrent features — that keep interactions fast.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Environment Setup: Profile the Slow Interaction\",\n      \"text\": \"Open React DevTools → Profiler, enable \\\"Record why each component rendered while profiling\\\" in settings, start recording, perform the interaction, and stop.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Capture a Baseline\",\n      \"text\": \"Record the commit duration for the interaction and the number of components that rendered.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Isolate Wasted and Expensive Renders\",\n      \"text\": \"Isolate Wasted and Expensive Renders\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Apply: Render Less, Render Cheaper, Render Later\",\n      \"text\": \"Render less by colocating state (move it down to where it is used), splitting context, and memoizing expensive subtrees (or adopting the React Compiler).\"\n    }\n  ]\n}\n",[1222,1227,1228],{"type":1224},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"React Rendering Performance: Making Updates Cheap\",\n  \"description\": \"How React decides what to re-render, how to find wasted and expensive renders, and the tools — memoization, the compiler, state design and concurrent features — that keep interactions fast.\",\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\u002Freact-rendering-performance\u002F\"\n  }\n}\n",[1222,1230,1231],{"type":1224},"\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\": \"React Rendering Performance\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fframework-performance\u002Freact-rendering-performance\u002F\"\n    }\n  ]\n}\n",[1233,1234,1235],"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 .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}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":275,"searchDepth":288,"depth":288,"links":1237},[1238,1239,1240,1241,1242,1243,1244,1245,1246,1247,1248,1249,1250,1251,1252,1253,1254,1255,1256,1257],{"id":208,"depth":288,"text":209},{"id":226,"depth":288,"text":227},{"id":243,"depth":288,"text":244},{"id":250,"depth":288,"text":251},{"id":257,"depth":288,"text":258},{"id":448,"depth":288,"text":449},{"id":690,"depth":288,"text":691},{"id":729,"depth":288,"text":730},{"id":766,"depth":288,"text":767},{"id":825,"depth":288,"text":826},{"id":835,"depth":288,"text":836},{"id":842,"depth":288,"text":843},{"id":962,"depth":288,"text":963},{"id":984,"depth":288,"text":985},{"id":995,"depth":288,"text":996},{"id":1006,"depth":288,"text":1007},{"id":1031,"depth":288,"text":1032},{"id":1067,"depth":288,"text":1068},{"id":1155,"depth":288,"text":1156},{"id":1196,"depth":288,"text":1197},"How React decides what to re-render, how to find wasted and expensive renders, and the tools — memoization, the compiler, state design and concurrent features — that keep interactions fast.","md",{"slug":1261,"type":1262,"breadcrumb":1263,"datePublished":1270,"dateModified":1270},"react-rendering-performance","topic",[1264,1267,1268],{"name":1265,"url":1266},"Home","\u002F",{"name":22,"url":21},{"name":5,"url":1269},"\u002Fframework-performance\u002Freact-rendering-performance\u002F","2026-10-06",true,"\u002Fframework-performance\u002Freact-rendering-performance",{"title":1274,"description":1275},"React Rendering Performance: Faster Updates and INP","Find and fix slow React updates: wasted renders, memoization that helps, the React Compiler, context cascades, and transitions that keep typing responsive.","framework-performance\u002Freact-rendering-performance\u002Findex","EaiT6qhsSAnxF2zYiOqsfaRqgJaQz1dvpQWErHso3lw",[1279,1283],{"title":1280,"path":1281,"stem":1282,"children":-1},"Reducing Next.js First Load JavaScript","\u002Fframework-performance\u002Fnextjs-performance\u002Freducing-nextjs-first-load-javascript","framework-performance\u002Fnextjs-performance\u002Freducing-nextjs-first-load-javascript\u002Findex",{"title":1178,"path":1284,"stem":1285,"children":-1},"\u002Fframework-performance\u002Freact-rendering-performance\u002Fadopting-the-react-compiler","framework-performance\u002Freact-rendering-performance\u002Fadopting-the-react-compiler\u002Findex",1791308069638]