[{"data":1,"prerenderedAt":1359},["ShallowReactive",2],{"content:\u002Fframework-performance\u002Fnextjs-performance":3,"surroundings:\u002Fframework-performance\u002Fnextjs-performance":1350},{"id":4,"title":5,"body":6,"description":1330,"extension":1331,"meta":1332,"navigation":1343,"path":1344,"seo":1345,"stem":1348,"__hash__":1349},"content\u002Fframework-performance\u002Fnextjs-performance\u002Findex.md","Next.js Performance",{"type":7,"value":8,"toc":1307},"minimark",[9,14,24,44,148,153,173,177,204,208,213,302,306,309,313,325,329,353,529,605,609,627,630,634,643,653,659,669,682,686,689,741,745,772,776,787,791,822,932,936,942,959,963,973,977,986,990,997,1000,1004,1044,1048,1088,1092,1104,1113,1128,1137,1149,1158,1167,1179,1188,1197,1206,1215,1224,1228,1265,1269,1292,1297,1300,1303],[10,11,13],"h1",{"id":12},"nextjs-performance-using-the-frameworks-tools-well","Next.js Performance: Using the Framework's Tools Well",[15,16,17,18,23],"p",{},"This topic is part of ",[19,20,22],"a",{"href":21},"\u002Fframework-performance\u002F","Framework Performance",". Next.js gives React applications server rendering, static generation, streaming, image and font optimisation, script loading strategies, route-level code splitting and, with the App Router, React Server Components. Used well, these produce pages with fast LCP, minimal client JavaScript and good INP. Used carelessly, a Next.js app can ship hundreds of kilobytes of JavaScript per page, hydrate the whole tree, lazy-load its LCP image, flash fallback fonts and run tag managers during hydration.",[15,25,26,27,31,32,35,36,39,40,43],{},"The most common Next.js performance problems come from a few patterns: ",[28,29,30],"code",{},"\"use client\""," directives placed high in the tree (turning large parts of the app into client components), large dependencies imported into shared layouts, ",[28,33,34],{},"next\u002Fimage"," used without ",[28,37,38],{},"priority"," or ",[28,41,42],{},"sizes"," on the LCP image, third-party scripts loaded with the wrong strategy, and dynamic rendering (or uncached data fetching) where static or cached output would do. Each has a framework-native fix.",[15,45,46],{},[47,48,54,55,54,62,54,66,54,69,54,78,54,84,54,92,54,99,54,104,54,110,54,114,54,117,54,120,54,124,54,127,54,131,54,135,54,138,54,141,54,145,54],"svg",{"viewBox":49,"width":50,"role":51,"ariaLabel":52,"style":53},"0 0 760 318","100%","img","Layers of a Next.js page and the framework features that optimise each.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[56,57],"rect",{"className":58,"x":60,"y":60,"width":50,"height":50,"fill":61},[59],"svg-canvas","0","#ffffff",[63,64,65],"title",{},"Next.js performance levers by layer",[67,68,52],"desc",{},[56,70],{"x":71,"y":71,"width":72,"height":73,"rx":74,"fill":75,"stroke":76,"style":77},"1","758","316","10","none","currentColor","stroke-opacity:0.18",[79,80,65],"text",{"x":81,"y":82,"fill":76,"style":83},"28.0","34.0","font-size:16px;font-weight:700",[56,85],{"x":81,"y":86,"width":87,"height":88,"rx":89,"fill":90,"stroke":90,"style":91},"56.0","704.0","40.0","6","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[79,93,98],{"x":94,"y":95,"fill":76,"style":96,"textAnchor":97},"42.0","80.5","font-size:13px;font-weight:700","start","Rendering mode",[79,100,103],{"x":101,"y":95,"fill":76,"style":102,"textAnchor":97},"189.3","font-size:12px","Static, dynamic, ISR, partial prerendering",[56,105],{"x":81,"y":106,"width":87,"height":88,"rx":89,"fill":107,"stroke":108,"style":109},"106.0","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[79,111,113],{"x":94,"y":112,"fill":76,"style":96,"textAnchor":97},"130.5","Component boundary",[79,115,116],{"x":101,"y":112,"fill":76,"style":102,"textAnchor":97},"Server Components by default; client only where needed",[56,118],{"x":81,"y":119,"width":87,"height":88,"rx":89,"fill":90,"stroke":90,"style":91},"156.0",[79,121,123],{"x":94,"y":122,"fill":76,"style":96,"textAnchor":97},"180.5","Assets",[79,125,126],{"x":101,"y":122,"fill":76,"style":102,"textAnchor":97},"next\u002Fimage, next\u002Ffont, next\u002Fscript",[56,128],{"x":81,"y":129,"width":87,"height":88,"rx":89,"fill":76,"stroke":76,"style":130},"206.0","fill-opacity:0.06;stroke-opacity:0.4",[79,132,134],{"x":94,"y":133,"fill":76,"style":96,"textAnchor":97},"230.5","Data",[79,136,137],{"x":101,"y":133,"fill":76,"style":102,"textAnchor":97},"Server fetching, caching and revalidation",[56,139],{"x":81,"y":140,"width":87,"height":88,"rx":89,"fill":76,"stroke":76,"style":130},"256.0",[79,142,144],{"x":94,"y":143,"fill":76,"style":96,"textAnchor":97},"280.5","Navigation",[79,146,147],{"x":101,"y":143,"fill":76,"style":102,"textAnchor":97},"Link prefetching and route-level code splitting",[149,150,152],"h2",{"id":151},"the-metric-degradation-this-topic-addresses","The Metric Degradation This Topic Addresses",[15,154,155,156,160,161,164,165,168,169,172],{},"Next.js apps typically struggle with ",[157,158,159],"strong",{},"INP"," and ",[157,162,163],{},"TBT"," when client JavaScript is large and hydration is heavy, with ",[157,166,167],{},"LCP"," when the hero image is misconfigured or content depends on client-side fetching, and with ",[157,170,171],{},"CLS"," when fonts swap without size-adjusted fallbacks or images lack dimensions. The build output's \"First Load JS\" column shows client JavaScript per route; values above 150–200KB compressed usually indicate room for improvement. In the field, segment metrics by route.",[149,174,176],{"id":175},"prerequisites","Prerequisites",[178,179,180,188,194,201],"ul",{},[181,182,183,184,187],"li",{},"The build output (",[28,185,186],{},"next build",") with First Load JS per route.",[181,189,190,193],{},[28,191,192],{},"@next\u002Fbundle-analyzer"," for chunk contents.",[181,195,196,197,200],{},"Field Core Web Vitals per route (via ",[28,198,199],{},"useReportWebVitals"," or a RUM tool).",[181,202,203],{},"Knowledge of which routes are static, dynamic or ISR.",[149,205,207],{"id":206},"_1-environment-setup-read-the-build-output","1. Environment Setup: Read the Build Output",[15,209,210,212],{},[28,211,186],{}," prints each route with its rendering mode (static, SSG, dynamic) and First Load JS. Note routes with large First Load JS and dynamic routes that could be static.",[214,215,220],"pre",{"className":216,"code":217,"language":218,"meta":219,"style":219},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F next.config.js — enable the bundle analyzer when ANALYZE=true.\nconst withBundleAnalyzer = require('@next\u002Fbundle-analyzer')({ enabled: process.env.ANALYZE === 'true' });\nmodule.exports = withBundleAnalyzer({ reactStrictMode: true });\n\u002F\u002F trade-off: the analyzer adds build time; run it on demand, not in every CI build.\n","javascript","",[28,221,222,231,272,296],{"__ignoreMap":219},[223,224,227],"span",{"class":225,"line":226},"line",1,[223,228,230],{"class":229},"sjfSM","\u002F\u002F next.config.js — enable the bundle analyzer when ANALYZE=true.\n",[223,232,234,238,242,245,249,253,257,260,263,266,269],{"class":225,"line":233},2,[223,235,237],{"class":236},"sPARh","const",[223,239,241],{"class":240},"sPXB4"," withBundleAnalyzer",[223,243,244],{"class":236}," =",[223,246,248],{"class":247},"smZ65"," require",[223,250,252],{"class":251},"saISM","(",[223,254,256],{"class":255},"sZ8jY","'@next\u002Fbundle-analyzer'",[223,258,259],{"class":251},")({ enabled: process.env.",[223,261,262],{"class":240},"ANALYZE",[223,264,265],{"class":236}," ===",[223,267,268],{"class":255}," 'true'",[223,270,271],{"class":251}," });\n",[223,273,275,278,281,284,286,288,291,294],{"class":225,"line":274},3,[223,276,277],{"class":240},"module",[223,279,280],{"class":251},".",[223,282,283],{"class":240},"exports",[223,285,244],{"class":236},[223,287,241],{"class":247},[223,289,290],{"class":251},"({ reactStrictMode: ",[223,292,293],{"class":240},"true",[223,295,271],{"class":251},[223,297,299],{"class":225,"line":298},4,[223,300,301],{"class":229},"\u002F\u002F trade-off: the analyzer adds build time; run it on demand, not in every CI build.\n",[149,303,305],{"id":304},"_2-capture-a-baseline","2. Capture a Baseline",[15,307,308],{},"Record First Load JS per key route, LCP\u002FINP\u002FCLS per route in the field, and lab traces of hydration on a throttled profile.",[149,310,312],{"id":311},"_3-isolate-the-causes","3. Isolate the Causes",[15,314,315,316,318,319,321,322,324],{},"For large bundles, find heavy dependencies and ",[28,317,30],{}," boundaries. For slow LCP, check the LCP element: is it a ",[28,320,34],{}," with ",[28,323,38],{},"? Does it depend on client data? For CLS, check fonts and images. For INP, profile interactions and hydration.",[149,326,328],{"id":327},"_4-apply-framework-native-fixes","4. Apply Framework-Native Fixes",[15,330,331,332,334,335,338,339,341,342,344,345,348,349,352],{},"Push ",[28,333,30],{}," down to leaf components; keep layouts and content as Server Components. Load heavy client components with ",[28,336,337],{},"next\u002Fdynamic",". Mark the LCP image with ",[28,340,38],{}," and correct ",[28,343,42],{},". Use ",[28,346,347],{},"next\u002Ffont"," for fonts. Load third-party scripts with ",[28,350,351],{},"next\u002Fscript"," strategies. Prefer static rendering and cached fetches; use partial prerendering for pages that mix static and dynamic content.",[214,354,356],{"className":216,"code":355,"language":218,"meta":219,"style":219},"\u002F\u002F Leaf-level client component: only the interactive part ships JavaScript.\n\u002F\u002F app\u002Fproduct\u002F[id]\u002Fpage.js (Server Component)\nimport AddToCart from '.\u002FAddToCart';          \u002F\u002F 'use client' inside AddToCart.js\nexport default async function Page({ params }) {\n  const product = await getProduct(params.id); \u002F\u002F runs on the server\n  return (\u003Carticle>\u003Ch1>{product.name}\u003C\u002Fh1>\u003Cp>{product.description}\u003C\u002Fp>\u003CAddToCart id={product.id} \u002F>\u003C\u002Farticle>);\n}\n\u002F\u002F trade-off: Server Components cannot use state or effects; split interactive\n\u002F\u002F parts into small client components and pass serialisable props.\n",[28,357,358,363,368,388,412,435,511,517,523],{"__ignoreMap":219},[223,359,360],{"class":225,"line":226},[223,361,362],{"class":229},"\u002F\u002F Leaf-level client component: only the interactive part ships JavaScript.\n",[223,364,365],{"class":225,"line":233},[223,366,367],{"class":229},"\u002F\u002F app\u002Fproduct\u002F[id]\u002Fpage.js (Server Component)\n",[223,369,370,373,376,379,382,385],{"class":225,"line":274},[223,371,372],{"class":236},"import",[223,374,375],{"class":251}," AddToCart ",[223,377,378],{"class":236},"from",[223,380,381],{"class":255}," '.\u002FAddToCart'",[223,383,384],{"class":251},";          ",[223,386,387],{"class":229},"\u002F\u002F 'use client' inside AddToCart.js\n",[223,389,390,393,396,399,402,405,409],{"class":225,"line":298},[223,391,392],{"class":236},"export",[223,394,395],{"class":236}," default",[223,397,398],{"class":236}," async",[223,400,401],{"class":236}," function",[223,403,404],{"class":247}," Page",[223,406,408],{"class":407},"sQw3B","({ params }) ",[223,410,411],{"class":251},"{\n",[223,413,415,418,421,423,426,429,432],{"class":225,"line":414},5,[223,416,417],{"class":236},"  const",[223,419,420],{"class":240}," product",[223,422,244],{"class":236},[223,424,425],{"class":236}," await",[223,427,428],{"class":247}," getProduct",[223,430,431],{"class":251},"(params.id); ",[223,433,434],{"class":229},"\u002F\u002F runs on the server\n",[223,436,438,441,444,448,451,453,456,459,462,465,468,470,472,474,476,478,481,483,485,487,489,492,495,498,501,503,506,508],{"class":225,"line":437},6,[223,439,440],{"class":236},"  return",[223,442,443],{"class":251}," (\u003C",[223,445,447],{"class":446},"sZBmE","article",[223,449,450],{"class":251},">\u003C",[223,452,10],{"class":446},[223,454,455],{"class":251},">",[223,457,458],{"class":236},"{",[223,460,461],{"class":251},"product.name",[223,463,464],{"class":236},"}",[223,466,467],{"class":251},"\u003C\u002F",[223,469,10],{"class":446},[223,471,450],{"class":251},[223,473,15],{"class":446},[223,475,455],{"class":251},[223,477,458],{"class":236},[223,479,480],{"class":251},"product.description",[223,482,464],{"class":236},[223,484,467],{"class":251},[223,486,15],{"class":446},[223,488,450],{"class":251},[223,490,491],{"class":446},"AddToCart",[223,493,494],{"class":240}," id",[223,496,497],{"class":236},"={",[223,499,500],{"class":251},"product.id",[223,502,464],{"class":236},[223,504,505],{"class":251}," \u002F>\u003C\u002F",[223,507,447],{"class":446},[223,509,510],{"class":251},">);\n",[223,512,514],{"class":225,"line":513},7,[223,515,516],{"class":251},"}\n",[223,518,520],{"class":225,"line":519},8,[223,521,522],{"class":229},"\u002F\u002F trade-off: Server Components cannot use state or effects; split interactive\n",[223,524,526],{"class":225,"line":525},9,[223,527,528],{"class":229},"\u002F\u002F parts into small client components and pass serialisable props.\n",[15,530,531],{},[47,532,54,535,54,538,54,541,54,543,54,546,54,548,54,555,54,562,54,567,54,571,54,575,54,579,54,583,54,588,54,592,54,596,54,601,54],{"viewBox":533,"width":50,"role":51,"ariaLabel":534,"style":53},"0 0 760 194","Bar chart of compressed first-load JavaScript for a Next.js product route before and after moving client boundaries down and deferring heavy components.",[56,536],{"className":537,"x":60,"y":60,"width":50,"height":50,"fill":61},[59],[63,539,540],{},"First Load JS for a product route",[67,542,534],{},[56,544],{"x":71,"y":71,"width":72,"height":545,"rx":74,"fill":75,"stroke":76,"style":77},"192",[79,547,540],{"x":81,"y":82,"fill":76,"style":83},[79,549,554],{"x":550,"y":551,"fill":76,"style":552,"textAnchor":553},"236.2","70.0","font-size:13px","end","Pages Router, all client",[56,556],{"x":557,"y":86,"width":558,"height":559,"rx":560,"fill":75,"stroke":76,"style":561},"248.2","419.8","19","3","fill-opacity:0.7;stroke-opacity:0.35",[79,563,566],{"x":564,"y":551,"fill":76,"style":565},"674.0","font-size:12px;font-weight:600","285KB",[79,568,570],{"x":550,"y":569,"fill":76,"style":552,"textAnchor":553},"101.0","App Router, 'use client' in layout",[56,572],{"x":557,"y":573,"width":574,"height":559,"rx":560,"fill":75,"stroke":76,"style":561},"87.0","353.5",[79,576,578],{"x":577,"y":569,"fill":76,"style":565},"607.7","240KB",[79,580,582],{"x":550,"y":581,"fill":76,"style":552,"textAnchor":553},"132.0","Client boundary at leaf components",[56,584],{"x":557,"y":585,"width":586,"height":559,"rx":560,"fill":90,"stroke":90,"style":587},"118.0","188.5","fill-opacity:0.55;stroke-opacity:0.9",[79,589,591],{"x":590,"y":581,"fill":76,"style":565},"442.8","128KB",[79,593,595],{"x":550,"y":594,"fill":76,"style":552,"textAnchor":553},"163.0","+ dynamic import for reviews widget",[56,597],{"x":557,"y":598,"width":599,"height":559,"rx":560,"fill":107,"stroke":108,"style":600},"149.0","141.4","fill-opacity:0.7;stroke-opacity:0.9",[79,602,604],{"x":603,"y":594,"fill":76,"style":565},"395.6","96KB",[149,606,608],{"id":607},"deconstructing-nextjs-rendering-modes","Deconstructing Next.js Rendering Modes",[15,610,611,614,615,618,619,622,623,626],{},[157,612,613],{},"Static rendering"," produces HTML at build time (or on first request with caching); it is the fastest and should be the default for content pages. ",[157,616,617],{},"Dynamic rendering"," runs per request, triggered by uncached data fetches or request-specific APIs (cookies, headers, search params). ",[157,620,621],{},"Incremental Static Regeneration"," (revalidation) regenerates static pages in the background at intervals or on demand. ",[157,624,625],{},"Partial prerendering"," combines a static shell, served instantly, with dynamic holes streamed in via Suspense — ideal for pages that are mostly static with a few personalised parts.",[15,628,629],{},"A page becomes dynamic as soon as any part of it uses request-time data, which can silently turn a cacheable page into one rendered on every request. Check the build output after changes, and isolate dynamic parts with Suspense boundaries (and partial prerendering) rather than letting them make the whole route dynamic.",[149,631,633],{"id":632},"advanced-diagnostics-and-edge-cases","Advanced Diagnostics and Edge Cases",[15,635,636,639,640,642],{},[157,637,638],{},"Client boundaries pulling in dependencies."," A ",[28,641,30],{}," file imports everything it uses into the client bundle, including large libraries used only for formatting.",[15,644,645,648,649,652],{},[157,646,647],{},"Barrel files."," Importing from index files that re-export many modules can pull more code than needed; ",[28,650,651],{},"optimizePackageImports"," helps for some libraries.",[15,654,655,658],{},[157,656,657],{},"Middleware cost."," Middleware runs on every matched request; heavy logic there adds to TTFB.",[15,660,661,664,665,668],{},[157,662,663],{},"Image optimisation cold misses."," The image optimiser transforms on first request; cache results and use appropriate ",[28,666,667],{},"minimumCacheTTL"," or an external loader with a CDN.",[15,670,671,54,674,677,678,681],{},[157,672,673],{},"Prefetching.",[28,675,676],{},"Link"," prefetches routes in the viewport in production, which can generate many requests on link-dense pages; adjust ",[28,679,680],{},"prefetch"," per link where needed.",[149,683,685],{"id":684},"validation-and-budgeting","Validation and Budgeting",[15,687,688],{},"Track First Load JS per route in CI (fail on growth beyond a threshold), LCP and INP per route in the field, and the static\u002Fdynamic status of key routes from build output.",[690,691,692,705],"table",{},[693,694,695],"thead",{},[696,697,698,702],"tr",{},[699,700,701],"th",{},"Budget",[699,703,704],{},"Target",[706,707,708,717,725,733],"tbody",{},[696,709,710,714],{},[711,712,713],"td",{},"First Load JS (key routes, compressed)",[711,715,716],{},"\u003C 130 KB",[696,718,719,722],{},[711,720,721],{},"LCP image",[711,723,724],{},"priority + correct sizes",[696,726,727,730],{},[711,728,729],{},"Fonts",[711,731,732],{},"next\u002Ffont with fallback adjustment",[696,734,735,738],{},[711,736,737],{},"Static routes",[711,739,740],{},"Content pages static or ISR",[149,742,744],{"id":743},"worked-example-a-media-companys-article-pages","Worked Example: A Media Company's Article Pages",[15,746,747,748,750,751,753,754,756,757,321,759,762,763,160,765,768,769,771],{},"A media company's Next.js article pages shipped 260KB of First Load JS because the root layout was a client component (it used a theme hook) and imported a large analytics SDK and a comment widget. The hero image was a ",[28,749,34],{}," without ",[28,752,38],{},", and Google Fonts were loaded via a stylesheet link. LCP p75 was 3.1 seconds and INP p75 270ms. The team moved the theme toggle into a small client component, made the layout a Server Component, loaded the comments widget with ",[28,755,337],{}," when scrolled into view, moved the analytics SDK to ",[28,758,351],{},[28,760,761],{},"afterInteractive",", added ",[28,764,38],{},[28,766,767],{},"sizes=\"100vw\""," to the hero, and switched to ",[28,770,347],{},". First Load JS fell to 92KB, LCP p75 to 2.0 seconds, INP p75 to 150ms, and CLS from fonts disappeared.",[149,773,775],{"id":774},"caching-and-data-fetching","Caching and Data Fetching",[15,777,778,779,782,783,786],{},"Next.js caches fetch results and rendered output in several layers, and defaults have changed across versions. For performance, the important questions are: is this route's HTML cached and served statically or regenerated on demand, and are slow data sources cached on the server? Explicit ",[28,780,781],{},"revalidate"," settings, ",[28,784,785],{},"cache"," options on fetch, and tagging for on-demand revalidation let you keep most pages static while updating them when content changes. Uncached, per-request fetches of slow APIs are a common cause of slow TTFB in App Router apps.",[149,788,790],{"id":789},"third-party-scripts-and-nextjs","Third-Party Scripts and Next.js",[15,792,793,795,796,799,800,802,803,806,807,810,811,39,813,815,816,818,819,821],{},[28,794,351],{}," offers strategies: ",[28,797,798],{},"beforeInteractive"," (critical scripts injected early), ",[28,801,761],{}," (default; loads after hydration begins), ",[28,804,805],{},"lazyOnload"," (during idle time) and ",[28,808,809],{},"worker"," (experimental, via Partytown). Most analytics and tag managers belong in ",[28,812,761],{},[28,814,805],{},"; chat widgets and social embeds in ",[28,817,805],{}," or loaded on interaction. Scripts in ",[28,820,798],{}," should be rare, because they compete with hydration and the LCP.",[15,823,824],{},[47,825,54,828,54,831,54,834,54,836,54,839,54,841,54,845,54,851,54,855,54,860,54,863,54,867,54,871,54,874,54,876,54,879,54,881,54,884,54,887,54,890,54,892,54,895,54,897,54,900,54,903,54,906,54,908,54,911,54,913,54,916,54,919,54,922,54,924,54,927,54,929,54],{"viewBox":826,"width":50,"role":51,"ariaLabel":827,"style":53},"0 0 760 228","The next\u002Fscript loading strategies, when they run and what they suit.",[56,829],{"className":830,"x":60,"y":60,"width":50,"height":50,"fill":61},[59],[63,832,833],{},"next\u002Fscript strategies",[67,835,827],{},[56,837],{"x":71,"y":71,"width":72,"height":838,"rx":74,"fill":75,"stroke":76,"style":77},"226",[79,840,833],{"x":81,"y":82,"fill":76,"style":83},[56,842],{"x":81,"y":86,"width":843,"height":844,"rx":60,"fill":76,"stroke":76,"style":130},"119.0","30.0",[79,846,850],{"x":847,"y":848,"fill":76,"style":849,"textAnchor":97},"38.0","75.5","font-size:12.5px;font-weight:700","Strategy",[56,852],{"x":853,"y":86,"width":854,"height":844,"rx":60,"fill":76,"stroke":76,"style":130},"147.0","292.5",[79,856,859],{"x":857,"y":848,"fill":76,"style":849,"textAnchor":858},"293.2","middle","Runs",[56,861],{"x":862,"y":86,"width":854,"height":844,"rx":60,"fill":76,"stroke":76,"style":130},"439.5",[79,864,866],{"x":865,"y":848,"fill":76,"style":849,"textAnchor":858},"585.7","Suited to",[56,868],{"x":81,"y":869,"width":843,"height":844,"rx":60,"fill":75,"stroke":76,"style":870},"86.0","stroke-opacity:0.35",[79,872,798],{"x":847,"y":873,"fill":76,"style":849,"textAnchor":97},"105.5",[56,875],{"x":853,"y":869,"width":854,"height":844,"rx":60,"fill":75,"stroke":76,"style":870},[79,877,878],{"x":857,"y":873,"fill":76,"style":102,"textAnchor":858},"before hydration",[56,880],{"x":862,"y":869,"width":854,"height":844,"rx":60,"fill":76,"stroke":76,"style":130},[79,882,883],{"x":865,"y":873,"fill":76,"style":102,"textAnchor":858},"consent, critical polyfills",[56,885],{"x":81,"y":886,"width":843,"height":844,"rx":60,"fill":75,"stroke":76,"style":870},"116.0",[79,888,761],{"x":847,"y":889,"fill":76,"style":849,"textAnchor":97},"135.5",[56,891],{"x":853,"y":886,"width":854,"height":844,"rx":60,"fill":75,"stroke":76,"style":870},[79,893,894],{"x":857,"y":889,"fill":76,"style":102,"textAnchor":858},"after hydration starts",[56,896],{"x":862,"y":886,"width":854,"height":844,"rx":60,"fill":90,"stroke":90,"style":91},[79,898,899],{"x":865,"y":889,"fill":76,"style":102,"textAnchor":858},"analytics, tag managers",[56,901],{"x":81,"y":902,"width":843,"height":844,"rx":60,"fill":75,"stroke":76,"style":870},"146.0",[79,904,805],{"x":847,"y":905,"fill":76,"style":849,"textAnchor":97},"165.5",[56,907],{"x":853,"y":902,"width":854,"height":844,"rx":60,"fill":75,"stroke":76,"style":870},[79,909,910],{"x":857,"y":905,"fill":76,"style":102,"textAnchor":858},"browser idle time",[56,912],{"x":862,"y":902,"width":854,"height":844,"rx":60,"fill":90,"stroke":90,"style":91},[79,914,915],{"x":865,"y":905,"fill":76,"style":102,"textAnchor":858},"chat, social, non-critical tags",[56,917],{"x":81,"y":918,"width":843,"height":844,"rx":60,"fill":75,"stroke":76,"style":870},"176.0",[79,920,809],{"x":847,"y":921,"fill":76,"style":849,"textAnchor":97},"195.5",[56,923],{"x":853,"y":918,"width":854,"height":844,"rx":60,"fill":75,"stroke":76,"style":870},[79,925,926],{"x":857,"y":921,"fill":76,"style":102,"textAnchor":858},"in a web worker (experimental)",[56,928],{"x":862,"y":918,"width":854,"height":844,"rx":60,"fill":76,"stroke":76,"style":130},[79,930,931],{"x":865,"y":921,"fill":76,"style":102,"textAnchor":858},"compatible third-party scripts",[149,933,935],{"id":934},"server-components-in-practice","Server Components in Practice",[15,937,938,939,941],{},"The App Router makes every component a Server Component unless it, or a module that imports it, declares ",[28,940,30],{},". Server Components can fetch data directly, use server-only libraries and render content without adding to the client bundle. The performance discipline is to keep the client boundary at the leaves: a page renders mostly server components, and small client components handle interactions — a quantity picker, a favourite button, a filter panel. Data flows from the server as props, which must be serialisable.",[15,943,944,945,948,949,952,953,956,957,280],{},"Two patterns keep boundaries small. First, pass server-rendered content into client components as ",[28,946,947],{},"children"," rather than importing it inside them; the content then stays server-rendered even inside an interactive wrapper (for example, an accordion that expands server-rendered text). Second, split interactive behaviour out of shared UI components: a ",[28,950,951],{},"Card"," that renders content on the server and a ",[28,954,955],{},"CardActions"," client component for its buttons, rather than one client ",[28,958,951],{},[149,960,962],{"id":961},"navigation-performance","Navigation Performance",[15,964,965,966,968,969,972],{},"After the first load, Next.js navigations are client-side: the router fetches the next route's React Server Component payload and code, then renders it. ",[28,967,676],{}," prefetches routes as links enter the viewport in production — the static parts fully, dynamic routes partially down to the nearest loading boundary — so most navigations feel instant. Two things slow them down: large route payloads (lots of data serialised into the RSC payload) and dynamic routes without ",[28,970,971],{},"loading.js"," or Suspense boundaries, which wait for the server before showing anything. Keep payloads lean by passing only the props client components need, and add loading states so the shell of the next route appears immediately.",[149,974,976],{"id":975},"monitoring-nextjs-apps-in-the-field","Monitoring Next.js Apps in the Field",[15,978,979,981,982,985],{},[28,980,199],{}," (or the ",[28,983,984],{},"web-vitals"," library directly) reports LCP, INP, CLS, FCP and TTFB for the initial load; Next.js also reports custom metrics for hydration and route changes in some versions. Send these to your analytics with the route pattern (not the full URL) so you can aggregate by template. Field data should be the final judge: a route whose First Load JS dropped by half but whose INP did not move probably has a different bottleneck — a slow interaction handler, a third-party script or a large DOM — that deserves attention next.",[149,987,989],{"id":988},"middleware-and-edge-runtime","Middleware and Edge Runtime",[15,991,992,993,996],{},"Middleware runs before every matched request, at the edge in most deployments. It is ideal for cheap decisions — redirects, rewrites, setting a cookie for an experiment, choosing a locale — and harmful when it does slow work: fetching user data from a distant database, calling third-party APIs, or parsing large tokens on every request. Because middleware sits in front of static and cached pages too, a 100ms middleware adds 100ms to TTFB for every page, including those that would otherwise be served instantly from the edge. Keep middleware fast, scope its ",[28,994,995],{},"matcher"," to the routes that need it, and move expensive logic into the routes themselves where it can be cached or streamed.",[15,998,999],{},"Edge-runtime route handlers and pages start quickly but have limited APIs, and if they fetch data from a single-region database, each query pays the full round trip. Prefer the Node.js runtime near your data for data-heavy routes, and the edge for logic that can run with cached or local data.",[149,1001,1003],{"id":1002},"a-rollout-plan","A Rollout Plan",[1005,1006,1007,1010,1015,1020,1032,1038,1041],"ol",{},[181,1008,1009],{},"Read build output and bundle analysis; rank routes by First Load JS and traffic.",[181,1011,331,1012,1014],{},[28,1013,30],{}," boundaries down; keep layouts and content as Server Components.",[181,1016,1017,1018,280],{},"Defer heavy client components with ",[28,1019,337],{},[181,1021,1022,1023,1025,1026,1028,1029,1031],{},"Fix the LCP image (",[28,1024,38],{},", ",[28,1027,42],{},") and fonts (",[28,1030,347],{},").",[181,1033,1034,1035,1037],{},"Move third-party scripts to appropriate ",[28,1036,351],{}," strategies.",[181,1039,1040],{},"Make content routes static or ISR; use partial prerendering for mixed pages.",[181,1042,1043],{},"Add First Load JS budgets and per-route field monitoring.",[149,1045,1047],{"id":1046},"common-pitfalls","Common Pitfalls",[178,1049,1050,1058,1064,1072,1082],{},[181,1051,1052,1057],{},[157,1053,1054,1056],{},[28,1055,30],{}," in layouts."," Turns the whole subtree into client components.",[181,1059,1060,1063],{},[157,1061,1062],{},"LCP image without priority."," Lazy-loaded and discovered late.",[181,1065,1066,1069,1070,280],{},[157,1067,1068],{},"Fonts via external stylesheets."," Render-blocking and CLS-prone; use ",[28,1071,347],{},[181,1073,1074,1077,1078,1081],{},[157,1075,1076],{},"Accidental dynamic rendering."," One ",[28,1079,1080],{},"cookies()"," call makes a whole route dynamic.",[181,1083,1084,1087],{},[157,1085,1086],{},"Tag managers in beforeInteractive."," Compete with hydration and LCP.",[149,1089,1091],{"id":1090},"faq","FAQ",[1093,1094,1097,1101],"details",{"className":1095},[1096],"faq-item",[1098,1099,1100],"summary",{},"What is a good First Load JS size?",[15,1102,1103],{},"For content routes, under about 100–130KB compressed is a good target. App-like routes may need more, but every 100KB costs hundreds of milliseconds on mid-range phones.",[1093,1105,1107,1110],{"className":1106},[1096],[1098,1108,1109],{},"Does the App Router improve performance?",[15,1111,1112],{},"It enables Server Components, streaming and partial prerendering, which can greatly reduce client JavaScript. The gains depend on keeping client boundaries small.",[1093,1114,1116,1119],{"className":1115},[1096],[1098,1117,1118],{},"Should every image use next\u002Fimage?",[15,1120,1121,1122,1124,1125,1127],{},"It is a good default. Make sure the LCP image has ",[28,1123,38],{}," and that ",[28,1126,42],{}," matches the layout, or images will be oversized.",[1093,1129,1131,1134],{"className":1130},[1096],[1098,1132,1133],{},"Why is my page dynamic?",[15,1135,1136],{},"It uses request-time data (cookies, headers, search params) or uncached fetches. The build output shows which routes are dynamic.",[1093,1138,1140,1143],{"className":1139},[1096],[1098,1141,1142],{},"How do I measure Web Vitals in Next.js?",[15,1144,1145,1146,1148],{},"Use the ",[28,1147,199],{}," hook to send metrics to your analytics, or a RUM tool. Segment by route.",[1093,1150,1152,1155],{"className":1151},[1096],[1098,1153,1154],{},"Does Link prefetching hurt performance?",[15,1156,1157],{},"It speeds up navigations but adds requests. On link-dense pages, disable prefetch for low-value links.",[1093,1159,1161,1164],{"className":1160},[1096],[1098,1162,1163],{},"Is the Pages Router slow?",[15,1165,1166],{},"Not inherently, but every page's components hydrate on the client. The App Router allows shipping less JavaScript through Server Components.",[1093,1168,1170,1173],{"className":1169},[1096],[1098,1171,1172],{},"How do I find accidental client components?",[15,1174,1175,1176,1178],{},"Search for ",[28,1177,30],{}," directives and check what they import: any shared module imported from a client file becomes client code. The bundle analyzer shows which modules ended up in client chunks.",[1093,1180,1182,1185],{"className":1181},[1096],[1098,1183,1184],{},"Does Next.js cache everything by default?",[15,1186,1187],{},"Caching defaults have changed between versions. Check the documentation for your version, and set caching and revalidation explicitly for data and routes where performance matters.",[1093,1189,1191,1194],{"className":1190},[1096],[1098,1192,1193],{},"Is Vercel required for good Next.js performance?",[15,1195,1196],{},"No. Self-hosted and other platforms can serve Next.js well, provided static output and images are cached at a CDN and streaming is not buffered by proxies.",[1093,1198,1200,1203],{"className":1199},[1096],[1098,1201,1202],{},"What is the biggest single Next.js win?",[15,1204,1205],{},"For content-heavy apps, moving client boundaries to the leaves so most of the page is Server Components. For image-heavy pages, fixing the LCP image’s priority and sizes.",[1093,1207,1209,1212],{"className":1208},[1096],[1098,1210,1211],{},"Should I upgrade Next.js for performance?",[15,1213,1214],{},"Often yes — releases regularly improve bundling, caching and rendering. Compare build output and lab traces before and after upgrading, since defaults sometimes change.",[1093,1216,1218,1221],{"className":1217},[1096],[1098,1219,1220],{},"How do Server Actions affect performance?",[15,1222,1223],{},"They remove the need for client-side API code and keep logic on the server. Use transitions to show pending state while they run so interactions stay responsive.",[149,1225,1227],{"id":1226},"guides-in-this-topic","Guides in This Topic",[178,1229,1230,1237,1244,1251,1258],{},[181,1231,1232,1236],{},[19,1233,1235],{"href":1234},"\u002Fframework-performance\u002Fnextjs-performance\u002Freducing-nextjs-first-load-javascript\u002F","Reducing Next.js first load JavaScript"," — smaller client bundles.",[181,1238,1239,1243],{},[19,1240,1242],{"href":1241},"\u002Fframework-performance\u002Fnextjs-performance\u002Feliminating-font-cls-with-next-font\u002F","Eliminating font CLS with next\u002Ffont"," — self-hosted fonts without shifts.",[181,1245,1246,1250],{},[19,1247,1249],{"href":1248},"\u002Fframework-performance\u002Fnextjs-performance\u002Fnextjs-partial-prerendering\u002F","Next.js partial prerendering"," — static shells with dynamic holes.",[181,1252,1253,1257],{},[19,1254,1256],{"href":1255},"\u002Fframework-performance\u002Fnextjs-performance\u002Fchoosing-next-script-loading-strategies\u002F","Choosing next\u002Fscript loading strategies"," — third-party scripts without blocking.",[181,1259,1260,1264],{},[19,1261,1263],{"href":1262},"\u002Fframework-performance\u002Fnextjs-performance\u002Ffixing-slow-lcp-with-the-nextjs-image-component\u002F","Fixing slow LCP with the Next.js Image component"," — priority, sizes and loaders.",[149,1266,1268],{"id":1267},"related","Related",[178,1270,1271,1278,1285],{},[181,1272,1273,1277],{},[19,1274,1276],{"href":1275},"\u002Fframework-performance\u002Freact-rendering-performance\u002F","React rendering performance"," — rendering costs inside Next.js apps.",[181,1279,1280,1284],{},[19,1281,1283],{"href":1282},"\u002Fnetwork-protocol-optimization\u002Fstreaming-ssr-and-early-flush\u002Fstreaming-ssr-with-react-suspense\u002F","Streaming SSR with React Suspense"," — the streaming model Next.js builds on.",[181,1286,1287,1291],{},[19,1288,1290],{"href":1289},"\u002Fjavascript-bundle-optimization-code-splitting\u002F","JavaScript bundle optimization"," — general bundle techniques.",[1293,1294,1296],"script",{"type":1295},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Next.js Performance: Using the Framework's Tools Well\",\n  \"description\": \"How to get excellent Core Web Vitals from Next.js — rendering modes, client bundle size, images, fonts, third-party scripts and caching — with the framework's own tools.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Environment Setup: Read the Build Output\",\n      \"text\": \"next build prints each route with its rendering mode (static, SSG, dynamic) and First Load JS.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Capture a Baseline\",\n      \"text\": \"Record First Load JS per key route, LCP\u002FINP\u002FCLS per route in the field, and lab traces of hydration on a throttled profile.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Isolate the Causes\",\n      \"text\": \"For large bundles, find heavy dependencies and \\\"use client\\\" boundaries.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Apply Framework-Native Fixes\",\n      \"text\": \"Push \\\"use client\\\" down to leaf components; keep layouts and content as Server Components.\"\n    }\n  ]\n}\n",[1293,1298,1299],{"type":1295},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Next.js Performance: Using the Framework's Tools Well\",\n  \"description\": \"How to get excellent Core Web Vitals from Next.js — rendering modes, client bundle size, images, fonts, third-party scripts and caching — with the framework's own tools.\",\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\u002Fnextjs-performance\u002F\"\n  }\n}\n",[1293,1301,1302],{"type":1295},"\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\": \"Next.js Performance\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fframework-performance\u002Fnextjs-performance\u002F\"\n    }\n  ]\n}\n",[1304,1305,1306],"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 .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 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 .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 .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}",{"title":219,"searchDepth":233,"depth":233,"links":1308},[1309,1310,1311,1312,1313,1314,1315,1316,1317,1318,1319,1320,1321,1322,1323,1324,1325,1326,1327,1328,1329],{"id":151,"depth":233,"text":152},{"id":175,"depth":233,"text":176},{"id":206,"depth":233,"text":207},{"id":304,"depth":233,"text":305},{"id":311,"depth":233,"text":312},{"id":327,"depth":233,"text":328},{"id":607,"depth":233,"text":608},{"id":632,"depth":233,"text":633},{"id":684,"depth":233,"text":685},{"id":743,"depth":233,"text":744},{"id":774,"depth":233,"text":775},{"id":789,"depth":233,"text":790},{"id":934,"depth":233,"text":935},{"id":961,"depth":233,"text":962},{"id":975,"depth":233,"text":976},{"id":988,"depth":233,"text":989},{"id":1002,"depth":233,"text":1003},{"id":1046,"depth":233,"text":1047},{"id":1090,"depth":233,"text":1091},{"id":1226,"depth":233,"text":1227},{"id":1267,"depth":233,"text":1268},"How to get excellent Core Web Vitals from Next.js — rendering modes, client bundle size, images, fonts, third-party scripts and caching — with the framework's own tools.","md",{"slug":1333,"type":1334,"breadcrumb":1335,"datePublished":1342,"dateModified":1342},"nextjs-performance","topic",[1336,1339,1340],{"name":1337,"url":1338},"Home","\u002F",{"name":22,"url":21},{"name":5,"url":1341},"\u002Fframework-performance\u002Fnextjs-performance\u002F","2026-10-06",true,"\u002Fframework-performance\u002Fnextjs-performance",{"title":1346,"description":1347},"Next.js Performance: LCP, INP and First Load JS","Make Next.js apps fast: cut first-load JavaScript with Server Components, configure next\u002Fimage and next\u002Ffont, load scripts wisely, and use partial prerendering.","framework-performance\u002Fnextjs-performance\u002Findex","DFchN2dqQsktl1Xn8HjX-3m5DhQw3_KaWGabX4CxfqY",[1351,1355],{"title":1352,"path":1353,"stem":1354,"children":-1},"SvelteKit Preloading and Load Functions","\u002Fframework-performance\u002Fastro-and-sveltekit-performance\u002Fsveltekit-preloading-and-load-functions","framework-performance\u002Fastro-and-sveltekit-performance\u002Fsveltekit-preloading-and-load-functions\u002Findex",{"title":1356,"path":1357,"stem":1358,"children":-1},"Choosing next\u002Fscript Loading Strategies","\u002Fframework-performance\u002Fnextjs-performance\u002Fchoosing-next-script-loading-strategies","framework-performance\u002Fnextjs-performance\u002Fchoosing-next-script-loading-strategies\u002Findex",1791308070282]