[{"data":1,"prerenderedAt":980},["ShallowReactive",2],{"content:\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Freact-server-components-and-client-bundle-size":3,"surroundings:\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Freact-server-components-and-client-bundle-size":971},{"id":4,"title":5,"body":6,"description":953,"extension":954,"meta":955,"navigation":965,"path":966,"seo":967,"stem":969,"__hash__":970},"content\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Freact-server-components-and-client-bundle-size\u002Findex.md","React Server Components and Client Bundle Size",{"type":7,"value":8,"toc":937},"minimark",[9,13,33,39,152,157,200,204,218,229,235,241,303,307,312,556,559,563,667,670,674,681,685,688,751,755,761,765,775,779,809,813,822,828,834,840,844,856,865,874,883,895,899,922,927,930,933],[10,11,5],"h1",{"id":12},"react-server-components-and-client-bundle-size",[14,15,16,17,22,23,27,28,32],"p",{},"This guide belongs to ",[18,19,21],"a",{"href":20},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002F","Hydration & Partial Rehydration Strategies"," in ",[18,24,26],{"href":25},"\u002Fjavascript-bundle-optimization-code-splitting\u002F","JavaScript Bundle Optimization & Code Splitting",". React Server Components (RSC), the default in the Next.js App Router and supported by other frameworks, render on the server and send the browser a serialised description of their output — not their code. A server component that formats dates with a heavy library, renders markdown with a parser, or queries a database ships none of that code to the client. Only components marked with ",[29,30,31],"code",{},"\"use client\""," (and everything they import) become client JavaScript and hydrate.",[14,34,35,36,38],{},"That makes RSC one of the most effective bundle-reduction tools available to React applications — when the client boundary is placed well. Placed badly, a single ",[29,37,31],{}," near the top of the tree turns most of the page back into client code, and the app ships as much JavaScript as before, plus the RSC payload.",[14,40,41],{},[42,43,49,50,49,57,49,61,49,64,49,73,49,79,49,88,49,94,49,99,49,104,49,107,49,110,49,113,49,116,49,119,49,122,49,127,49,131,49,133,49,137,49,139,49,142,49,144,49,147,49,149,49],"svg",{"viewBox":44,"width":45,"role":46,"ariaLabel":47,"style":48},"0 0 760 228","100%","img","Comparison of a page whose client boundary sits at the layout level with one whose client components are small interactive leaves.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[51,52],"rect",{"className":53,"x":55,"y":55,"width":45,"height":45,"fill":56},[54],"svg-canvas","0","#ffffff",[58,59,60],"title",{},"Where the \"use client\" boundary sits",[62,63,47],"desc",{},[51,65],{"x":66,"y":66,"width":67,"height":68,"rx":69,"fill":70,"stroke":71,"style":72},"1","758","226","10","none","currentColor","stroke-opacity:0.18",[74,75,60],"text",{"x":76,"y":77,"fill":71,"style":78},"28.0","34.0","font-size:16px;font-weight:700",[51,80],{"x":76,"y":81,"width":82,"height":83,"rx":84,"fill":85,"stroke":86,"style":87},"56.0","340.0","150.0","6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[74,89,93],{"x":90,"y":91,"fill":71,"style":92},"42.0","82.0","font-size:14px;font-weight:700","Boundary at the top",[74,95,98],{"x":90,"y":96,"fill":71,"style":97},"108.0","font-size:12.5px;font-weight:700","•",[74,100,103],{"x":81,"y":96,"fill":71,"style":101,"textAnchor":102},"font-size:12.5px","start","Layout marked use client",[74,105,98],{"x":90,"y":106,"fill":71,"style":97},"132.0",[74,108,109],{"x":81,"y":106,"fill":71,"style":101,"textAnchor":102},"Everything below becomes client code",[74,111,98],{"x":90,"y":112,"fill":71,"style":97},"156.0",[74,114,115],{"x":81,"y":112,"fill":71,"style":101,"textAnchor":102},"Markdown parser + date lib shipped",[74,117,98],{"x":90,"y":118,"fill":71,"style":97},"180.0",[74,120,121],{"x":81,"y":118,"fill":71,"style":101,"textAnchor":102},"Client JS ~ same as before RSC",[51,123],{"x":124,"y":81,"width":82,"height":83,"rx":84,"fill":125,"stroke":125,"style":126},"392.0","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[74,128,130],{"x":129,"y":91,"fill":71,"style":92},"406.0","Boundaries at the leaves",[74,132,98],{"x":129,"y":96,"fill":71,"style":97},[74,134,136],{"x":135,"y":96,"fill":71,"style":101,"textAnchor":102},"420.0","Layout and content are server components",[74,138,98],{"x":129,"y":106,"fill":71,"style":97},[74,140,141],{"x":135,"y":106,"fill":71,"style":101,"textAnchor":102},"Only buttons, forms, menus are client",[74,143,98],{"x":129,"y":112,"fill":71,"style":97},[74,145,146],{"x":135,"y":112,"fill":71,"style":101,"textAnchor":102},"Heavy libraries stay on the server",[74,148,98],{"x":129,"y":118,"fill":71,"style":97},[74,150,151],{"x":135,"y":118,"fill":71,"style":101,"textAnchor":102},"Client JS cut by 50-80%",[153,154,156],"h2",{"id":155},"rapid-diagnosis","Rapid Diagnosis",[158,159,160,171,180,190],"ul",{},[161,162,163,49,167,170],"li",{},[164,165,166],"strong",{},"Read the build output.",[29,168,169],{},"next build"," lists \"First Load JS\" per route; compare routes with similar UI.",[161,172,173,179],{},[164,174,175,176,178],{},"Search for ",[29,177,31],{},"."," Each occurrence is a boundary; boundaries in layouts, providers or large page components are suspects.",[161,181,182,185,186,189],{},[164,183,184],{},"Analyse the client bundle"," with ",[29,187,188],{},"@next\u002Fbundle-analyzer"," and look for libraries that only render static output (markdown, syntax highlighting, date formatting, icons).",[161,191,192,195,196,199],{},[164,193,194],{},"Check the RSC payload size."," In the Network panel, RSC responses (",[29,197,198],{},"?_rsc="," requests or the inline flight data) can be large for data-heavy pages.",[153,201,203],{"id":202},"root-cause-analysis","Root Cause Analysis",[14,205,206,209,210,213,214,217],{},[164,207,208],{},"1. High client boundaries."," Marking a layout or page as a client component to use one hook (",[29,211,212],{},"useState"," for a menu, ",[29,215,216],{},"usePathname"," for active links) converts its entire subtree's imports into client code.",[14,219,220,223,224,228],{},[164,221,222],{},"2. Context providers at the root."," Providers must be client components; wrapping the whole app in a client provider is fine as long as children are passed as props (server components can be children of client components) — but importing server-only content ",[225,226,227],"em",{},"into"," a provider file makes it client code.",[14,230,231,234],{},[164,232,233],{},"3. Shared utility modules."," A utility imported by both server and client components ships to the client with all its dependencies, even if the client uses one function.",[14,236,237,240],{},[164,238,239],{},"4. Large serialised props."," Passing big data objects from server to client components inflates the RSC payload and the hydration work for those components.",[14,242,243],{},[42,244,49,247,49,250,49,253,49,255,49,258,49,260,49,267,49,274,49,279,49,283,49,287,49,291,49,294,49,299,49],{"viewBox":245,"width":45,"role":46,"ariaLabel":246,"style":48},"0 0 760 163","Bar chart of first-load JavaScript for a blog post route in a Pages Router app, an App Router app with a high client boundary, and one with leaf client components.",[51,248],{"className":249,"x":55,"y":55,"width":45,"height":45,"fill":56},[54],[58,251,252],{},"First Load JS for a blog post route",[62,254,246],{},[51,256],{"x":66,"y":66,"width":67,"height":257,"rx":69,"fill":70,"stroke":71,"style":72},"161",[74,259,252],{"x":76,"y":77,"fill":71,"style":78},[74,261,266],{"x":262,"y":263,"fill":71,"style":264,"textAnchor":265},"207.6","70.0","font-size:13px","end","Pages Router (all client)",[51,268],{"x":269,"y":81,"width":270,"height":271,"rx":272,"fill":85,"stroke":86,"style":273},"219.6","448.4","19","3","fill-opacity:0.7;stroke-opacity:0.9",[74,275,278],{"x":276,"y":263,"fill":71,"style":277},"674.0","font-size:12px;font-weight:600","214KB",[74,280,282],{"x":262,"y":281,"fill":71,"style":264,"textAnchor":265},"101.0","App Router, use client in layout",[51,284],{"x":269,"y":285,"width":286,"height":271,"rx":272,"fill":85,"stroke":86,"style":273},"87.0","414.8",[74,288,290],{"x":289,"y":281,"fill":71,"style":277},"640.5","198KB",[74,292,293],{"x":262,"y":106,"fill":71,"style":264,"textAnchor":265},"App Router, client leaves only",[51,295],{"x":269,"y":296,"width":297,"height":271,"rx":272,"fill":125,"stroke":125,"style":298},"118.0","192.8","fill-opacity:0.55;stroke-opacity:0.9",[74,300,302],{"x":301,"y":106,"fill":71,"style":277},"418.4","92KB",[153,304,306],{"id":305},"step-by-step-resolution","Step-by-Step Resolution",[308,309,311],"h3",{"id":310},"_1-push-use-client-down-to-interactive-leaves","1. Push \"use client\" down to interactive leaves",[313,314,319],"pre",{"className":315,"code":316,"language":317,"meta":318,"style":318},"language-jsx shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F app\u002Fblog\u002F[slug]\u002Fpage.jsx — a server component (no directive).\nimport { renderMarkdown } from '@\u002Flib\u002Fmarkdown';      \u002F\u002F heavy parser stays on the server\nimport { LikeButton } from '.\u002FLikeButton';            \u002F\u002F small client component\nexport default async function Post({ params }) {\n  const post = await getPost(params.slug);\n  return (\u003Carticle>\n    \u003Ch1>{post.title}\u003C\u002Fh1>\n    \u003Cdiv dangerouslySetInnerHTML={{ __html: await renderMarkdown(post.body) }} \u002F>\n    \u003CLikeButton postId={post.id} \u002F>\n  \u003C\u002Farticle>);\n}\n\u002F\u002F LikeButton.jsx starts with \"use client\" and contains only the button logic.\n\u002F\u002F trade-off: splitting interactivity into leaf components adds files and props\n\u002F\u002F plumbing. Worth it for content-heavy pages; less so for highly interactive\n\u002F\u002F dashboards where most of the tree is genuinely client-side.\n","jsx","",[29,320,321,330,354,373,399,421,437,464,495,515,526,532,538,544,550],{"__ignoreMap":318},[322,323,326],"span",{"class":324,"line":325},"line",1,[322,327,329],{"class":328},"sjfSM","\u002F\u002F app\u002Fblog\u002F[slug]\u002Fpage.jsx — a server component (no directive).\n",[322,331,333,337,341,344,348,351],{"class":324,"line":332},2,[322,334,336],{"class":335},"sPARh","import",[322,338,340],{"class":339},"saISM"," { renderMarkdown } ",[322,342,343],{"class":335},"from",[322,345,347],{"class":346},"sZ8jY"," '@\u002Flib\u002Fmarkdown'",[322,349,350],{"class":339},";      ",[322,352,353],{"class":328},"\u002F\u002F heavy parser stays on the server\n",[322,355,357,359,362,364,367,370],{"class":324,"line":356},3,[322,358,336],{"class":335},[322,360,361],{"class":339}," { LikeButton } ",[322,363,343],{"class":335},[322,365,366],{"class":346}," '.\u002FLikeButton'",[322,368,369],{"class":339},";            ",[322,371,372],{"class":328},"\u002F\u002F small client component\n",[322,374,376,379,382,385,388,392,396],{"class":324,"line":375},4,[322,377,378],{"class":335},"export",[322,380,381],{"class":335}," default",[322,383,384],{"class":335}," async",[322,386,387],{"class":335}," function",[322,389,391],{"class":390},"smZ65"," Post",[322,393,395],{"class":394},"sQw3B","({ params }) ",[322,397,398],{"class":339},"{\n",[322,400,402,405,409,412,415,418],{"class":324,"line":401},5,[322,403,404],{"class":335},"  const",[322,406,408],{"class":407},"sPXB4"," post",[322,410,411],{"class":335}," =",[322,413,414],{"class":335}," await",[322,416,417],{"class":390}," getPost",[322,419,420],{"class":339},"(params.slug);\n",[322,422,424,427,430,434],{"class":324,"line":423},6,[322,425,426],{"class":335},"  return",[322,428,429],{"class":339}," (\u003C",[322,431,433],{"class":432},"sZBmE","article",[322,435,436],{"class":339},">\n",[322,438,440,443,445,448,451,454,457,460,462],{"class":324,"line":439},7,[322,441,442],{"class":339},"    \u003C",[322,444,10],{"class":432},[322,446,447],{"class":339},">",[322,449,450],{"class":335},"{",[322,452,453],{"class":339},"post.title",[322,455,456],{"class":335},"}",[322,458,459],{"class":339},"\u003C\u002F",[322,461,10],{"class":432},[322,463,436],{"class":339},[322,465,467,469,472,475,478,481,484,487,490,492],{"class":324,"line":466},8,[322,468,442],{"class":339},[322,470,471],{"class":432},"div",[322,473,474],{"class":407}," dangerouslySetInnerHTML",[322,476,477],{"class":335},"={",[322,479,480],{"class":339},"{ __html: ",[322,482,483],{"class":335},"await",[322,485,486],{"class":390}," renderMarkdown",[322,488,489],{"class":339},"(post.body) }",[322,491,456],{"class":335},[322,493,494],{"class":339}," \u002F>\n",[322,496,498,500,503,506,508,511,513],{"class":324,"line":497},9,[322,499,442],{"class":339},[322,501,502],{"class":432},"LikeButton",[322,504,505],{"class":407}," postId",[322,507,477],{"class":335},[322,509,510],{"class":339},"post.id",[322,512,456],{"class":335},[322,514,494],{"class":339},[322,516,518,521,523],{"class":324,"line":517},10,[322,519,520],{"class":339},"  \u003C\u002F",[322,522,433],{"class":432},[322,524,525],{"class":339},">);\n",[322,527,529],{"class":324,"line":528},11,[322,530,531],{"class":339},"}\n",[322,533,535],{"class":324,"line":534},12,[322,536,537],{"class":328},"\u002F\u002F LikeButton.jsx starts with \"use client\" and contains only the button logic.\n",[322,539,541],{"class":324,"line":540},13,[322,542,543],{"class":328},"\u002F\u002F trade-off: splitting interactivity into leaf components adds files and props\n",[322,545,547],{"class":324,"line":546},14,[322,548,549],{"class":328},"\u002F\u002F plumbing. Worth it for content-heavy pages; less so for highly interactive\n",[322,551,553],{"class":324,"line":552},15,[322,554,555],{"class":328},"\u002F\u002F dashboards where most of the tree is genuinely client-side.\n",[14,557,558],{},"Expected outcome: the markdown library and the article rendering code disappear from the client bundle.",[308,560,562],{"id":561},"_2-pass-server-components-as-children-of-client-providers","2. Pass server components as children of client providers",[313,564,566],{"className":315,"code":565,"language":317,"meta":318,"style":318},"\u002F\u002F app\u002Flayout.jsx (server component)\nimport { ThemeProvider } from '.\u002FThemeProvider';    \u002F\u002F \"use client\" inside\nexport default function RootLayout({ children }) {\n  return \u003Chtml>\u003Cbody>\u003CThemeProvider>{children}\u003C\u002FThemeProvider>\u003C\u002Fbody>\u003C\u002Fhtml>;\n}\n\u002F\u002F trade-off: anything the provider file itself imports becomes client code.\n\u002F\u002F Keep provider files small and import nothing but the context they provide.\n",[29,567,568,573,591,607,653,657,662],{"__ignoreMap":318},[322,569,570],{"class":324,"line":325},[322,571,572],{"class":328},"\u002F\u002F app\u002Flayout.jsx (server component)\n",[322,574,575,577,580,582,585,588],{"class":324,"line":332},[322,576,336],{"class":335},[322,578,579],{"class":339}," { ThemeProvider } ",[322,581,343],{"class":335},[322,583,584],{"class":346}," '.\u002FThemeProvider'",[322,586,587],{"class":339},";    ",[322,589,590],{"class":328},"\u002F\u002F \"use client\" inside\n",[322,592,593,595,597,599,602,605],{"class":324,"line":356},[322,594,378],{"class":335},[322,596,381],{"class":335},[322,598,387],{"class":335},[322,600,601],{"class":390}," RootLayout",[322,603,604],{"class":394},"({ children }) ",[322,606,398],{"class":339},[322,608,609,611,614,617,620,623,625,628,630,632,635,637,639,641,644,646,648,650],{"class":324,"line":375},[322,610,426],{"class":335},[322,612,613],{"class":339}," \u003C",[322,615,616],{"class":432},"html",[322,618,619],{"class":339},">\u003C",[322,621,622],{"class":432},"body",[322,624,619],{"class":339},[322,626,627],{"class":432},"ThemeProvider",[322,629,447],{"class":339},[322,631,450],{"class":335},[322,633,634],{"class":339},"children",[322,636,456],{"class":335},[322,638,459],{"class":339},[322,640,627],{"class":432},[322,642,643],{"class":339},">\u003C\u002F",[322,645,622],{"class":432},[322,647,643],{"class":339},[322,649,616],{"class":432},[322,651,652],{"class":339},">;\n",[322,654,655],{"class":324,"line":401},[322,656,531],{"class":339},[322,658,659],{"class":324,"line":423},[322,660,661],{"class":328},"\u002F\u002F trade-off: anything the provider file itself imports becomes client code.\n",[322,663,664],{"class":324,"line":439},[322,665,666],{"class":328},"\u002F\u002F Keep provider files small and import nothing but the context they provide.\n",[14,668,669],{},"Expected outcome: the provider is client-side; the pages inside it remain server components.",[308,671,673],{"id":672},"_3-split-shared-utilities-by-environment","3. Split shared utilities by environment",[14,675,676,677,680],{},"Separate server-only utilities (",[29,678,679],{},"import 'server-only'",") from client-safe ones so imports cannot leak heavy server code into client bundles, and so accidental imports fail the build.",[308,682,684],{"id":683},"_4-trim-props-crossing-the-boundary","4. Trim props crossing the boundary",[14,686,687],{},"Pass client components only the fields they render. Large objects serialised into the RSC payload cost bytes and parse time even though no component code ships.",[14,689,690],{},[42,691,49,694,49,697,49,700,49,702,49,705,49,707,49,711,49,716,49,721,49,725,49,729,49,732,49,734,49,738,49,741,49,744,49,748,49],{"viewBox":692,"width":45,"role":46,"ariaLabel":693,"style":48},"0 0 760 268","Layers of what is sent to the browser for a page built with React Server Components.",[51,695],{"className":696,"x":55,"y":55,"width":45,"height":45,"fill":56},[54],[58,698,699],{},"What reaches the browser in an RSC app",[62,701,693],{},[51,703],{"x":66,"y":66,"width":67,"height":704,"rx":69,"fill":70,"stroke":71,"style":72},"266",[74,706,699],{"x":76,"y":77,"fill":71,"style":78},[51,708],{"x":76,"y":81,"width":709,"height":710,"rx":84,"fill":125,"stroke":125,"style":126},"704.0","40.0",[74,712,715],{"x":90,"y":713,"fill":71,"style":714},"80.5","font-size:13px;font-weight:700","HTML",[74,717,720],{"x":718,"y":713,"fill":71,"style":719,"textAnchor":102},"185.8","font-size:12px","server-rendered markup for the whole page (fast first paint)",[51,722],{"x":76,"y":723,"width":709,"height":710,"rx":84,"fill":71,"stroke":71,"style":724},"106.0","fill-opacity:0.06;stroke-opacity:0.4",[74,726,728],{"x":90,"y":727,"fill":71,"style":714},"130.5","RSC payload",[74,730,731],{"x":718,"y":727,"fill":71,"style":719,"textAnchor":102},"serialised server component output and props for client components",[51,733],{"x":76,"y":112,"width":709,"height":710,"rx":84,"fill":85,"stroke":86,"style":87},[74,735,737],{"x":90,"y":736,"fill":71,"style":714},"180.5","Client component JS",[74,739,740],{"x":718,"y":736,"fill":71,"style":719,"textAnchor":102},"only modules reachable from use client boundaries",[51,742],{"x":76,"y":743,"width":709,"height":710,"rx":84,"fill":125,"stroke":125,"style":126},"206.0",[74,745,747],{"x":90,"y":746,"fill":71,"style":714},"230.5","Hydration work",[74,749,750],{"x":718,"y":746,"fill":71,"style":719,"textAnchor":102},"only client components hydrate; server output is not re-rendered",[153,752,754],{"id":753},"verification","Verification",[14,756,757,758,760],{},"Compare ",[29,759,169],{}," First Load JS per route before and after, and confirm in the bundle analyser that heavy libraries no longer appear in client chunks. In a throttled trace, hydration should be limited to small client components, and TBT should fall. Check the RSC payload size has not ballooned in exchange — if it has, trim props.",[153,762,764],{"id":763},"worked-example-a-documentation-site-migration","Worked Example: A Documentation Site Migration",[14,766,767,768,770,771,774],{},"A documentation site migrated from the Pages Router to the App Router but initially put ",[29,769,31],{}," in its docs layout to keep a sidebar's expand\u002Fcollapse state. First Load JS stayed at 205KB per page, because the layout imported the MDX renderer, syntax highlighter and search index client-side. Moving the sidebar state into a small ",[29,772,773],{},"SidebarToggle"," client component and keeping the layout as a server component dropped First Load JS to 88KB; lab TBT on docs pages fell from 380ms to 120ms, and field INP p75 improved from 230ms to 150ms.",[153,776,778],{"id":777},"common-mistakes","Common Mistakes",[158,780,781,787,797,803],{},[161,782,783,786],{},[164,784,785],{},"Marking a page client-side to use one hook."," Extract the hook's UI into a leaf component instead.",[161,788,789,792,793,796],{},[164,790,791],{},"Importing server utilities into client components."," Use the ",[29,794,795],{},"server-only"," package to fail fast.",[161,798,799,802],{},[164,800,801],{},"Fetching on the client what could be fetched on the server."," Client data fetching brings fetch libraries and loading states into the bundle.",[161,804,805,808],{},[164,806,807],{},"Ignoring the RSC payload."," It is not free; very large payloads delay hydration and use bandwidth.",[153,810,812],{"id":811},"edge-cases","Edge Cases",[14,814,815,818,819,821],{},[164,816,817],{},"Third-party components without directives."," Libraries that use hooks but lack ",[29,820,31],{}," must be wrapped in your own client component before use in server components.",[14,823,824,827],{},[164,825,826],{},"Context-heavy libraries."," UI libraries that rely on React context throughout may force large client boundaries; prefer libraries that work with server components or isolate them to interactive regions.",[14,829,830,833],{},[164,831,832],{},"Streaming and Suspense."," Server components can stream with Suspense, improving TTFB and LCP; client components inside streamed regions hydrate when their code and data arrive.",[14,835,836,839],{},[164,837,838],{},"Caching."," Server components shift work to the server; without caching, TTFB can rise. Cache data and use static rendering where possible.",[153,841,843],{"id":842},"faq","FAQ",[845,846,849,853],"details",{"className":847},[848],"faq-item",[850,851,852],"summary",{},"Do server components eliminate hydration?",[14,854,855],{},"For server components, yes — their output is not hydrated. Client components still hydrate. The total hydration cost becomes proportional to the interactive part of the page instead of the whole page.",[845,857,859,862],{"className":858},[848],[850,860,861],{},"Is the RSC payload larger than the JavaScript it replaces?",[14,863,864],{},"Usually much smaller, because it contains rendered output rather than code and libraries. It can grow large for pages that pass big data objects to client components; measure it alongside JavaScript size.",[845,866,868,871],{"className":867},[848],[850,869,870],{},"Does this help INP or only load time?",[14,872,873],{},"Both. Less client code means less parse, compile and hydration work during load, which reduces input delay for early interactions. Smaller client trees also re-render faster on state changes.",[845,875,877,880],{"className":876},[848],[850,878,879],{},"Can I adopt server components incrementally?",[14,881,882],{},"In Next.js, the App Router and Pages Router coexist, so routes can migrate one at a time. Within an App Router route, start by keeping pages as server components and extracting interactive pieces into client components.",[845,884,886,889],{"className":885},[848],[850,887,888],{},"How do I find which import pulled a library into the client bundle?",[14,890,891,892,894],{},"Use the bundle analyser to find the library in a client chunk, then search for imports of it in files reachable from ",[29,893,31],{}," boundaries. The import chain usually leads to a shared utility or a component that should have stayed on the server.",[153,896,898],{"id":897},"related","Related",[158,900,901,908,915],{},[161,902,903,907],{},[18,904,906],{"href":905},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Ffixing-long-hydration-tasks-in-nextjs-app-router\u002F","Fixing long hydration tasks in Next.js App Router"," — hydration cost after boundaries are placed.",[161,909,910,914],{},[18,911,913],{"href":912},"\u002Fframework-performance\u002Fnextjs-performance\u002Freducing-nextjs-first-load-javascript\u002F","Reducing Next.js first load JavaScript"," — other levers for the same metric.",[161,916,917,921],{},[18,918,920],{"href":919},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Fislands-vs-full-hydration-for-content-sites\u002F","Islands vs full hydration for content sites"," — a related architecture outside React.",[923,924,926],"script",{"type":925},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"React Server Components and Client Bundle Size\",\n  \"description\": \"How React Server Components change what reaches the browser, how client boundaries leak bundle size, and how to verify the reduction.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Push \\\"use client\\\" down to interactive leaves\",\n      \"text\": \"Expected outcome: the markdown library and the article rendering code disappear from the client bundle.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Pass server components as children of client providers\",\n      \"text\": \"Expected outcome: the provider is client-side; the pages inside it remain server components.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Split shared utilities by environment\",\n      \"text\": \"Separate server-only utilities (import 'server-only') from client-safe ones so imports cannot leak heavy server code into client bundles, and so accidental imports fail the build.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Trim props crossing the boundary\",\n      \"text\": \"Pass client components only the fields they render.\"\n    }\n  ]\n}\n",[923,928,929],{"type":925},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"React Server Components and Client Bundle Size\",\n  \"description\": \"How React Server Components change what reaches the browser, how client boundaries leak bundle size, and how to verify the reduction.\",\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\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Freact-server-components-and-client-bundle-size\u002F\"\n  }\n}\n",[923,931,932],{"type":925},"\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\": \"JavaScript Bundle Optimization & Code Splitting\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Hydration & Partial Rehydration Strategies\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"React Server Components and Client Bundle Size\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Freact-server-components-and-client-bundle-size\u002F\"\n    }\n  ]\n}\n",[934,935,936],"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 .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}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 .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":318,"searchDepth":332,"depth":332,"links":938},[939,940,941,947,948,949,950,951,952],{"id":155,"depth":332,"text":156},{"id":202,"depth":332,"text":203},{"id":305,"depth":332,"text":306,"children":942},[943,944,945,946],{"id":310,"depth":356,"text":311},{"id":561,"depth":356,"text":562},{"id":672,"depth":356,"text":673},{"id":683,"depth":356,"text":684},{"id":753,"depth":332,"text":754},{"id":763,"depth":332,"text":764},{"id":777,"depth":332,"text":778},{"id":811,"depth":332,"text":812},{"id":842,"depth":332,"text":843},{"id":897,"depth":332,"text":898},"How React Server Components change what reaches the browser, how client boundaries leak bundle size, and how to verify the reduction.","md",{"slug":12,"type":433,"breadcrumb":956,"datePublished":964,"dateModified":964},[957,960,961,962],{"name":958,"url":959},"Home","\u002F",{"name":26,"url":25},{"name":21,"url":20},{"name":5,"url":963},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Freact-server-components-and-client-bundle-size\u002F","2026-10-06",true,"\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Freact-server-components-and-client-bundle-size",{"title":5,"description":968},"Server Components ship zero JavaScript for non-interactive UI. Learn where the \"use client\" boundary belongs, what still ships, and how to measure the savings.","javascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Freact-server-components-and-client-bundle-size\u002Findex","1v5tD0GPNpO7TiISjGPlrp06KBZod4RMTXjK9fJhOa8",[972,976],{"title":973,"path":974,"stem":975},"Islands vs Full Hydration for Content Sites","\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Fislands-vs-full-hydration-for-content-sites","javascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Fislands-vs-full-hydration-for-content-sites\u002Findex",{"title":977,"path":978,"stem":979},"Resumability vs Hydration","\u002Fjavascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Fresumability-vs-hydration","javascript-bundle-optimization-code-splitting\u002Fhydration-and-partial-rehydration-strategies\u002Fresumability-vs-hydration\u002Findex",1791308076083]