[{"data":1,"prerenderedAt":1024},["ShallowReactive",2],{"content:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fedge-side-includes-vs-client-side-fragments":3,"surroundings:\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fedge-side-includes-vs-client-side-fragments":1015},{"id":4,"title":5,"body":6,"description":995,"extension":996,"meta":997,"navigation":1009,"path":1010,"seo":1011,"stem":1013,"__hash__":1014},"content\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fedge-side-includes-vs-client-side-fragments\u002Findex.md","Edge Side Includes vs Client-Side Fragments",{"type":7,"value":8,"toc":979},"minimark",[9,14,29,32,145,150,179,183,189,195,201,207,336,340,345,348,352,527,530,534,659,662,666,669,800,804,807,811,814,818,844,848,854,860,866,872,876,888,897,906,919,928,937,941,964,969,972,975],[10,11,13],"h1",{"id":12},"edge-side-includes-vs-client-side-fragments-where-to-assemble-the-page","Edge Side Includes vs Client-Side Fragments: Where to Assemble the Page",[15,16,17,18,23,24,28],"p",{},"This comparison sits within ",[19,20,22],"a",{"href":21},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002F","Edge Compute & Dynamic Caching",", part of ",[19,25,27],{"href":26},"\u002Fadvanced-caching-strategies-cdn-architecture\u002F","Advanced Caching Strategies & CDN Architecture",". Once a page is split into a cacheable shell and fragments that cannot share its cache lifetime — a personal header, a live stock badge, a recommendation rail — something has to put them back together. There are two places to do it: at the edge, before the HTML reaches the browser (Edge Side Includes, or an edge function streaming fragments into the shell), or in the browser, after the shell has painted (JavaScript fetching fragments and inserting them).",[15,30,31],{},"Both keep the shell cached. They differ in what the first paint contains, how failures behave, and which Core Web Vitals they affect. Edge assembly produces a complete first paint but makes the response depend on fragment latency. Client assembly makes the first paint fast and independent of fragments, at the cost of a later visual completion and layout-shift risk.",[15,33,34],{},[35,36,42,43,42,50,42,54,42,57,42,66,42,72,42,81,42,87,42,92,42,97,42,100,42,103,42,106,42,109,42,112,42,115,42,120,42,124,42,126,42,130,42,132,42,135,42,137,42,140,42,142,42],"svg",{"viewBox":37,"width":38,"role":39,"ariaLabel":40,"style":41},"0 0 760 228","100%","img","Comparison of assembling page fragments at the edge versus in the browser, by first paint content and failure behaviour.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[44,45],"rect",{"className":46,"x":48,"y":48,"width":38,"height":38,"fill":49},[47],"svg-canvas","0","#ffffff",[51,52,53],"title",{},"Edge assembly vs client assembly",[55,56,40],"desc",{},[44,58],{"x":59,"y":59,"width":60,"height":61,"rx":62,"fill":63,"stroke":64,"style":65},"1","758","226","10","none","currentColor","stroke-opacity:0.18",[67,68,53],"text",{"x":69,"y":70,"fill":64,"style":71},"28.0","34.0","font-size:16px;font-weight:700",[44,73],{"x":69,"y":74,"width":75,"height":76,"rx":77,"fill":78,"stroke":79,"style":80},"56.0","340.0","150.0","6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[67,82,86],{"x":83,"y":84,"fill":64,"style":85},"42.0","82.0","font-size:14px;font-weight:700","Edge assembly (ESI \u002F streaming)",[67,88,91],{"x":83,"y":89,"fill":64,"style":90},"108.0","font-size:12.5px;font-weight:700","•",[67,93,96],{"x":74,"y":89,"fill":64,"style":94,"textAnchor":95},"font-size:12.5px","start","Complete HTML in the first paint",[67,98,91],{"x":83,"y":99,"fill":64,"style":90},"132.0",[67,101,102],{"x":74,"y":99,"fill":64,"style":94,"textAnchor":95},"No layout shift from fragments",[67,104,91],{"x":83,"y":105,"fill":64,"style":90},"156.0",[67,107,108],{"x":74,"y":105,"fill":64,"style":94,"textAnchor":95},"TTFB or stream waits on fragments",[67,110,91],{"x":83,"y":111,"fill":64,"style":90},"180.0",[67,113,114],{"x":74,"y":111,"fill":64,"style":94,"textAnchor":95},"Works without JavaScript",[44,116],{"x":117,"y":74,"width":75,"height":76,"rx":77,"fill":118,"stroke":118,"style":119},"392.0","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[67,121,123],{"x":122,"y":84,"fill":64,"style":85},"406.0","Client assembly (fetch + insert)",[67,125,91],{"x":122,"y":89,"fill":64,"style":90},[67,127,129],{"x":128,"y":89,"fill":64,"style":94,"textAnchor":95},"420.0","Shell paints immediately",[67,131,91],{"x":122,"y":99,"fill":64,"style":90},[67,133,134],{"x":128,"y":99,"fill":64,"style":94,"textAnchor":95},"Fragments appear later — reserve space",[67,136,91],{"x":122,"y":105,"fill":64,"style":90},[67,138,139],{"x":128,"y":105,"fill":64,"style":94,"textAnchor":95},"TTFB independent of fragments",[67,141,91],{"x":122,"y":111,"fill":64,"style":90},[67,143,144],{"x":128,"y":111,"fill":64,"style":94,"textAnchor":95},"Requires JavaScript; extra request",[146,147,149],"h2",{"id":148},"rapid-diagnosis","Rapid Diagnosis",[151,152,153,161,167,173],"ul",{},[154,155,156,160],"li",{},[157,158,159],"strong",{},"Identify fragments and their latency."," Measure each fragment endpoint's p75 and p95 response time from the edge and from clients.",[154,162,163,166],{},[157,164,165],{},"Check whether fragments are in the LCP region."," A fragment that contains or precedes the LCP element is a strong candidate for edge assembly.",[154,168,169,172],{},[157,170,171],{},"Check layout-shift risk."," Client-inserted fragments above other content shift layout unless space is reserved.",[154,174,175,178],{},[157,176,177],{},"Check streaming support."," Edge assembly only keeps TTFB low if the platform streams the shell while fragments load.",[146,180,182],{"id":181},"root-cause-analysis","Root Cause Analysis",[15,184,185,188],{},[157,186,187],{},"1. Fragment latency becomes page latency at the edge."," With classic ESI, the edge waits for each include before sending what follows; a slow fragment delays everything after it.",[15,190,191,194],{},[157,192,193],{},"2. Late insertion causes CLS on the client."," Fragments inserted into unreserved space push content down after the user has started reading.",[15,196,197,200],{},[157,198,199],{},"3. Client fragments depend on JavaScript."," If the script fails or is delayed by main-thread work, the fragment appears late or not at all.",[15,202,203,206],{},[157,204,205],{},"4. Duplicate work."," Some setups render fragments at the edge and then re-fetch them on the client for hydration, paying twice.",[15,208,209],{},[35,210,42,213,42,216,42,219,42,221,42,224,42,226,42,230,42,237,42,244,42,248,42,252,42,256,42,260,42,264,42,267,42,270,42,273,42,277,42,280,42,282,42,285,42,288,42,292,42,296,42,300,42,307,42,313,42,317,42,321,42,325,42,329,42,332,42],{"viewBox":211,"width":38,"role":39,"ariaLabel":212,"style":41},"0 0 760 216","Timelines comparing edge assembly that waits for a fragment, streaming edge assembly, and client-side assembly after the shell paints.",[44,214],{"className":215,"x":48,"y":48,"width":38,"height":38,"fill":49},[47],[51,217,218],{},"Page assembly timelines",[55,220,212],{},[44,222],{"x":59,"y":59,"width":60,"height":223,"rx":62,"fill":63,"stroke":64,"style":65},"214",[67,225,218],{"x":69,"y":70,"fill":64,"style":71},[67,227,229],{"x":69,"y":228,"fill":64,"style":90},"74.0","Classic ESI",[44,231],{"x":232,"y":74,"width":233,"height":234,"rx":235,"fill":64,"stroke":64,"style":236},"134.3","38.0","26.0","3","fill-opacity:0.06;stroke-opacity:0.4",[67,238,243],{"x":239,"y":240,"fill":64,"style":241,"textAnchor":242},"153.3","73.5","font-size:11.5px","middle","edge",[44,245],{"x":246,"y":74,"width":247,"height":234,"rx":235,"fill":78,"stroke":79,"style":80},"173.8","146.6",[67,249,251],{"x":250,"y":240,"fill":64,"style":241,"textAnchor":242},"247.1","wait fragment",[44,253],{"x":254,"y":74,"width":255,"height":234,"rx":235,"fill":64,"stroke":64,"style":236},"321.9","205.9",[67,257,259],{"x":258,"y":240,"fill":64,"style":241,"textAnchor":242},"424.8","HTML",[67,261,263],{"x":69,"y":262,"fill":64,"style":90},"112.0","Streaming edge",[44,265],{"x":232,"y":266,"width":233,"height":234,"rx":235,"fill":64,"stroke":64,"style":236},"94.0",[67,268,243],{"x":239,"y":269,"fill":64,"style":241,"textAnchor":242},"111.5",[44,271],{"x":246,"y":266,"width":272,"height":234,"rx":235,"fill":64,"stroke":64,"style":236},"354.0",[67,274,276],{"x":275,"y":269,"fill":64,"style":241,"textAnchor":242},"350.8","shell streams; fragment slots in at 380ms",[67,278,279],{"x":69,"y":76,"fill":64,"style":90},"Client fetch",[44,281],{"x":232,"y":99,"width":233,"height":234,"rx":235,"fill":64,"stroke":64,"style":236},[67,283,243],{"x":239,"y":284,"fill":64,"style":241,"textAnchor":242},"149.5",[44,286],{"x":246,"y":99,"width":287,"height":234,"rx":235,"fill":64,"stroke":64,"style":236},"304.6",[67,289,291],{"x":290,"y":284,"fill":64,"style":241,"textAnchor":242},"326.1","shell paints",[44,293],{"x":294,"y":99,"width":295,"height":234,"rx":235,"fill":78,"stroke":79,"style":80},"578.6","97.2",[67,297,299],{"x":298,"y":284,"fill":64,"style":241,"textAnchor":242},"627.3","fragment",[301,302],"line",{"x1":303,"y1":304,"x2":305,"y2":304,"stroke":64,"style":306},"133.5","172.0","726.0","stroke-opacity:0.4",[67,308,312],{"x":309,"y":310,"fill":311,"style":241,"textAnchor":242},"144.2","189.0","#51617a","0ms",[67,314,316],{"x":315,"y":310,"fill":311,"style":241,"textAnchor":242},"232.3","200ms",[67,318,320],{"x":319,"y":310,"fill":311,"style":241,"textAnchor":242},"331.0","400ms",[67,322,324],{"x":323,"y":310,"fill":311,"style":241,"textAnchor":242},"429.8","600ms",[67,326,328],{"x":327,"y":310,"fill":311,"style":241,"textAnchor":242},"528.5","800ms",[67,330,331],{"x":298,"y":310,"fill":311,"style":241,"textAnchor":242},"1000ms",[67,333,335],{"x":334,"y":310,"fill":311,"style":241,"textAnchor":242},"712.3","1200ms",[146,337,339],{"id":338},"step-by-step-choosing-and-implementing","Step-by-Step: Choosing and Implementing",[341,342,344],"h3",{"id":343},"_1-classify-fragments-by-position-and-latency","1. Classify fragments by position and latency",[15,346,347],{},"Fragments in the first viewport that affect layout (a personalised banner above the hero) favour edge assembly. Fragments below the fold or in fixed-size slots (cart count in the header, recommendations rail) suit client assembly.",[341,349,351],{"id":350},"_2-stream-edge-assembled-fragments-rather-than-blocking","2. Stream edge-assembled fragments rather than blocking",[353,354,359],"pre",{"className":355,"code":356,"language":357,"meta":358,"style":358},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F Edge function: stream the shell, fetch the fragment in parallel, slot it in.\nconst fragment = fetch(`${ORIGIN}\u002Ffragments\u002Fbanner?u=${userId}`).then((r) => r.text());\nreturn new HTMLRewriter()\n  .on('#personal-banner', { async element(el) { el.setInnerContent(await fragment, { html: true }); } })\n  .transform(cachedShell);\n\u002F\u002F trade-off: the stream pauses at the slot until the fragment resolves, so\n\u002F\u002F content after the slot is delayed by the fragment's latency. Place slots late\n\u002F\u002F in the document, or set a timeout and leave the default content.\n","javascript","",[360,361,362,370,435,450,498,509,515,521],"code",{"__ignoreMap":358},[363,364,366],"span",{"class":301,"line":365},1,[363,367,369],{"class":368},"sjfSM","\u002F\u002F Edge function: stream the shell, fetch the fragment in parallel, slot it in.\n",[363,371,373,377,381,384,388,392,396,399,402,405,408,411,414,417,421,424,427,430,432],{"class":301,"line":372},2,[363,374,376],{"class":375},"sPARh","const",[363,378,380],{"class":379},"sPXB4"," fragment",[363,382,383],{"class":375}," =",[363,385,387],{"class":386},"smZ65"," fetch",[363,389,391],{"class":390},"saISM","(",[363,393,395],{"class":394},"sZ8jY","`${",[363,397,398],{"class":379},"ORIGIN",[363,400,401],{"class":394},"}\u002Ffragments\u002Fbanner?u=${",[363,403,404],{"class":390},"userId",[363,406,407],{"class":394},"}`",[363,409,410],{"class":390},").",[363,412,413],{"class":386},"then",[363,415,416],{"class":390},"((",[363,418,420],{"class":419},"sQw3B","r",[363,422,423],{"class":390},") ",[363,425,426],{"class":375},"=>",[363,428,429],{"class":390}," r.",[363,431,67],{"class":386},[363,433,434],{"class":390},"());\n",[363,436,438,441,444,447],{"class":301,"line":437},3,[363,439,440],{"class":375},"return",[363,442,443],{"class":375}," new",[363,445,446],{"class":386}," HTMLRewriter",[363,448,449],{"class":390},"()\n",[363,451,453,456,459,461,464,467,470,473,475,478,481,484,486,489,492,495],{"class":301,"line":452},4,[363,454,455],{"class":390},"  .",[363,457,458],{"class":386},"on",[363,460,391],{"class":390},[363,462,463],{"class":394},"'#personal-banner'",[363,465,466],{"class":390},", { ",[363,468,469],{"class":375},"async",[363,471,472],{"class":386}," element",[363,474,391],{"class":390},[363,476,477],{"class":419},"el",[363,479,480],{"class":390},") { el.",[363,482,483],{"class":386},"setInnerContent",[363,485,391],{"class":390},[363,487,488],{"class":375},"await",[363,490,491],{"class":390}," fragment, { html: ",[363,493,494],{"class":379},"true",[363,496,497],{"class":390}," }); } })\n",[363,499,501,503,506],{"class":301,"line":500},5,[363,502,455],{"class":390},[363,504,505],{"class":386},"transform",[363,507,508],{"class":390},"(cachedShell);\n",[363,510,512],{"class":301,"line":511},6,[363,513,514],{"class":368},"\u002F\u002F trade-off: the stream pauses at the slot until the fragment resolves, so\n",[363,516,518],{"class":301,"line":517},7,[363,519,520],{"class":368},"\u002F\u002F content after the slot is delayed by the fragment's latency. Place slots late\n",[363,522,524],{"class":301,"line":523},8,[363,525,526],{"class":368},"\u002F\u002F in the document, or set a timeout and leave the default content.\n",[15,528,529],{},"Expected outcome: shell bytes before the slot arrive at cache speed; only content after the slot waits.",[341,531,533],{"id":532},"_3-insert-client-fragments-into-reserved-space","3. Insert client fragments into reserved space",[353,535,537],{"className":355,"code":536,"language":357,"meta":358,"style":358},"const slot = document.querySelector('[data-fragment=\"recs\"]');     \u002F\u002F has min-height in CSS\nfetch('\u002Ffragments\u002Frecs', { credentials: 'same-origin' })\n  .then((r) => r.text())\n  .then((html) => { slot.innerHTML = html; })\n  .catch(() => slot.remove());\n\u002F\u002F trade-off: removing a failed slot shifts content below it. Keep the reserved\n\u002F\u002F space with a neutral message instead if the slot is above other content.\n",[360,538,539,565,584,605,629,649,654],{"__ignoreMap":358},[363,540,541,543,546,548,551,554,556,559,562],{"class":301,"line":365},[363,542,376],{"class":375},[363,544,545],{"class":379}," slot",[363,547,383],{"class":375},[363,549,550],{"class":390}," document.",[363,552,553],{"class":386},"querySelector",[363,555,391],{"class":390},[363,557,558],{"class":394},"'[data-fragment=\"recs\"]'",[363,560,561],{"class":390},");     ",[363,563,564],{"class":368},"\u002F\u002F has min-height in CSS\n",[363,566,567,570,572,575,578,581],{"class":301,"line":372},[363,568,569],{"class":386},"fetch",[363,571,391],{"class":390},[363,573,574],{"class":394},"'\u002Ffragments\u002Frecs'",[363,576,577],{"class":390},", { credentials: ",[363,579,580],{"class":394},"'same-origin'",[363,582,583],{"class":390}," })\n",[363,585,586,588,590,592,594,596,598,600,602],{"class":301,"line":437},[363,587,455],{"class":390},[363,589,413],{"class":386},[363,591,416],{"class":390},[363,593,420],{"class":419},[363,595,423],{"class":390},[363,597,426],{"class":375},[363,599,429],{"class":390},[363,601,67],{"class":386},[363,603,604],{"class":390},"())\n",[363,606,607,609,611,613,616,618,620,623,626],{"class":301,"line":452},[363,608,455],{"class":390},[363,610,413],{"class":386},[363,612,416],{"class":390},[363,614,615],{"class":419},"html",[363,617,423],{"class":390},[363,619,426],{"class":375},[363,621,622],{"class":390}," { slot.innerHTML ",[363,624,625],{"class":375},"=",[363,627,628],{"class":390}," html; })\n",[363,630,631,633,636,639,641,644,647],{"class":301,"line":500},[363,632,455],{"class":390},[363,634,635],{"class":386},"catch",[363,637,638],{"class":390},"(() ",[363,640,426],{"class":375},[363,642,643],{"class":390}," slot.",[363,645,646],{"class":386},"remove",[363,648,434],{"class":390},[363,650,651],{"class":301,"line":511},[363,652,653],{"class":368},"\u002F\u002F trade-off: removing a failed slot shifts content below it. Keep the reserved\n",[363,655,656],{"class":301,"line":517},[363,657,658],{"class":368},"\u002F\u002F space with a neutral message instead if the slot is above other content.\n",[15,660,661],{},"Expected outcome: no layout shift and a fast, fragment-independent first paint.",[341,663,665],{"id":664},"_4-set-timeouts-for-edge-fragments","4. Set timeouts for edge fragments",[15,667,668],{},"Never let a slow fragment hold the page hostage. Give each edge fragment a budget (for example 150ms) and fall back to the default content or to client loading if it is exceeded.",[15,670,671],{},[35,672,42,675,42,678,42,681,42,683,42,700,42,703,42,705,42,709,42,715,42,718,42,722,42,729,42,735,42,739,42,744,42,747,42,751,42,753,42,756,42,759,42,762,42,765,42,768,42,771,42,775,42,777,42,780,42,783,42,786,42,790,42,793,42,796,42],{"viewBox":673,"width":38,"role":39,"ariaLabel":674,"style":41},"0 0 760 325","Decision sequence for whether to assemble a fragment at the edge or in the browser.",[44,676],{"className":677,"x":48,"y":48,"width":38,"height":38,"fill":49},[47],[51,679,680],{},"Edge or client for this fragment?",[55,682,674],{},[684,685,686],"defs",{},[687,688,695],"marker",{"id":689,"viewBox":690,"refX":691,"refY":692,"markerWidth":693,"markerHeight":693,"orient":694},"fa99299e7b","0 0 10 10","9","5","7","auto-start-reverse",[696,697],"path",{"d":698,"fill":64,"style":699},"M0 0 L10 5 L0 10 z","fill-opacity:0.7",[44,701],{"x":59,"y":59,"width":60,"height":702,"rx":62,"fill":63,"stroke":64,"style":65},"323",[67,704,680],{"x":69,"y":70,"fill":64,"style":71},[44,706],{"x":69,"y":74,"width":707,"height":708,"rx":77,"fill":64,"stroke":64,"style":236},"320.0","35.0",[67,710,714],{"x":711,"y":712,"fill":64,"style":713,"textAnchor":242},"188.0","78.0","font-size:13px;font-weight:700","In the first viewport and changes layout?",[44,716],{"x":717,"y":74,"width":707,"height":708,"rx":77,"fill":118,"stroke":118,"style":119},"412.0",[67,719,721],{"x":720,"y":712,"fill":64,"style":94,"textAnchor":242},"572.0","Edge assembly with a timeout",[301,723],{"x1":724,"y1":240,"x2":725,"y2":240,"stroke":64,"strokeWidth":726,"style":727,"markerEnd":728},"348.0","410.0","1.5","stroke-opacity:0.6","url(#fa99299e7b)",[67,730,734],{"x":731,"y":732,"fill":118,"style":733,"textAnchor":242},"380.0","66.5","font-size:11.5px;font-weight:700","yes",[301,736],{"x1":711,"y1":737,"x2":711,"y2":738,"stroke":64,"strokeWidth":726,"style":727,"markerEnd":728},"91.0","125.0",[67,740,743],{"x":741,"y":742,"fill":311,"style":733},"196.0","113.0","no",[44,745],{"x":69,"y":746,"width":707,"height":708,"rx":77,"fill":64,"stroke":64,"style":236},"127.0",[67,748,750],{"x":711,"y":749,"fill":64,"style":713,"textAnchor":242},"149.0","Slow or unreliable endpoint?",[44,752],{"x":717,"y":746,"width":707,"height":708,"rx":77,"fill":78,"stroke":79,"style":80},[67,754,755],{"x":720,"y":749,"fill":64,"style":94,"textAnchor":242},"Client fetch into reserved space",[301,757],{"x1":724,"y1":758,"x2":725,"y2":758,"stroke":64,"strokeWidth":726,"style":727,"markerEnd":728},"144.5",[67,760,734],{"x":731,"y":761,"fill":118,"style":733,"textAnchor":242},"137.5",[301,763],{"x1":711,"y1":764,"x2":711,"y2":741,"stroke":64,"strokeWidth":726,"style":727,"markerEnd":728},"162.0",[67,766,743],{"x":741,"y":767,"fill":311,"style":733},"184.0",[44,769],{"x":69,"y":770,"width":707,"height":708,"rx":77,"fill":64,"stroke":64,"style":236},"198.0",[67,772,774],{"x":711,"y":773,"fill":64,"style":713,"textAnchor":242},"220.0","Must work without JavaScript?",[44,776],{"x":717,"y":770,"width":707,"height":708,"rx":77,"fill":118,"stroke":118,"style":119},[67,778,779],{"x":720,"y":773,"fill":64,"style":94,"textAnchor":242},"Edge assembly",[301,781],{"x1":724,"y1":782,"x2":725,"y2":782,"stroke":64,"strokeWidth":726,"style":727,"markerEnd":728},"215.5",[67,784,734],{"x":731,"y":785,"fill":118,"style":733,"textAnchor":242},"208.5",[301,787],{"x1":711,"y1":788,"x2":711,"y2":789,"stroke":64,"strokeWidth":726,"style":727,"markerEnd":728},"233.0","267.0",[67,791,743],{"x":741,"y":792,"fill":311,"style":733},"255.0",[44,794],{"x":69,"y":795,"width":707,"height":70,"rx":77,"fill":78,"stroke":79,"style":80},"269.0",[67,797,799],{"x":711,"y":798,"fill":64,"style":94,"textAnchor":242},"290.5","Client fetch after the shell paints",[146,801,803],{"id":802},"verification","Verification",[15,805,806],{},"Measure TTFB, LCP and CLS for each approach on a staging page. Edge assembly should show complete first paint and zero fragment-related CLS, with TTFB unaffected when streaming is configured correctly. Client assembly should show fast LCP and no CLS if space is reserved. Simulate a slow fragment (add a delay to the endpoint) and confirm the page degrades gracefully in both cases.",[146,808,810],{"id":809},"worked-example-a-media-homepage","Worked Example: A Media Homepage",[15,812,813],{},"A news homepage had three fragments: a personal \"Continue reading\" strip near the top, a live breaking-news ticker, and a recommendations rail far down the page. The team assembled the \"Continue reading\" strip at the edge with a 120ms timeout (falling back to a generic \"Popular now\" strip), streamed the ticker into its slot, and fetched recommendations on the client into a fixed-height rail. TTFB stayed at cache-hit levels (p75 85ms), LCP was unaffected by fragment latency, and CLS from the old client-inserted top strip — 0.08 at p75 — disappeared.",[146,815,817],{"id":816},"common-mistakes","Common Mistakes",[151,819,820,826,832,838],{},[154,821,822,825],{},[157,823,824],{},"Blocking ESI on slow fragments."," One slow include makes the entire page slow.",[154,827,828,831],{},[157,829,830],{},"Client-inserting above-the-fold content without reserved space."," Guaranteed CLS.",[154,833,834,837],{},[157,835,836],{},"Caching fragments with the shell."," Personal fragments must have their own (usually private or no) cache policy.",[154,839,840,843],{},[157,841,842],{},"No fallback."," A failed fragment should show sensible default content, never a broken layout.",[146,845,847],{"id":846},"edge-cases","Edge Cases",[15,849,850,853],{},[157,851,852],{},"ESI support varies."," Akamai and Fastly support ESI natively; Cloudflare and others implement equivalents through edge functions. Behaviour around timeouts, nesting and caching of includes differs by vendor.",[15,855,856,859],{},[157,857,858],{},"Hydration frameworks."," If the page hydrates, fragments assembled at the edge must match what the client renders, or you create hydration mismatches. Treat fragment regions as non-hydrated islands where possible.",[15,861,862,865],{},[157,863,864],{},"SEO."," Edge-assembled content is visible to crawlers that do not run JavaScript; client fragments may not be. Keep SEO-relevant content in the shell or edge fragments.",[15,867,868,871],{},[157,869,870],{},"Caching fragments themselves."," Fragments can have their own cache entries (for example, a ticker cached for 30 seconds), which makes edge assembly even cheaper.",[146,873,875],{"id":874},"faq","FAQ",[877,878,881,885],"details",{"className":879},[880],"faq-item",[882,883,884],"summary",{},"Is ESI still relevant with modern edge functions?",[15,886,887],{},"The concept is; the syntax less so. Edge functions with streaming HTML rewriters provide the same assembly with more control over timeouts, parallelism and fallbacks. ESI remains convenient on CDNs that support it natively.",[877,889,891,894],{"className":890},[880],[882,892,893],{},"Which approach is better for LCP?",[15,895,896],{},"Whichever keeps fragments out of the LCP element's critical path. If a fragment is the LCP element, edge assembly with a strict timeout is better; otherwise client assembly keeps LCP independent of fragment latency.",[877,898,900,903],{"className":899},[880],[882,901,902],{},"Can I combine both?",[15,904,905],{},"Yes — most real pages do: edge assembly for the few fragments that shape the first viewport, client assembly for everything else.",[877,907,909,912],{"className":908},[880],[882,910,911],{},"How do fragments affect INP?",[15,913,914,915,918],{},"Client fragments add a fetch and a DOM insertion, usually small. Large fragments inserted during user interaction can cause presentation delay; insert them when idle or use ",[360,916,917],{},"content-visibility"," for heavy below-the-fold fragments.",[877,920,922,925],{"className":921},[880],[882,923,924],{},"What about micro-frontends?",[15,926,927],{},"Micro-frontend composition is fragment assembly at a larger scale. The same trade-offs apply, with the added cost that each fragment may bring its own JavaScript; composing at the edge keeps HTML fast but does not reduce that JavaScript.",[877,929,931,934],{"className":930},[880],[882,932,933],{},"How do I measure fragment impact in the field?",[15,935,936],{},"Mark fragment arrival with User Timing (or Element Timing on the fragment's main element) and send it with your RUM beacon alongside LCP and CLS. Comparing fragment arrival time with LCP shows whether fragments are on the critical path, and CLS attribution shows whether client insertions shift layout.",[146,938,940],{"id":939},"related","Related",[151,942,943,950,957],{},[154,944,945,949],{},[19,946,948],{"href":947},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fpersonalizing-cached-pages-at-the-edge\u002F","Personalizing cached pages at the edge"," — rewriting small personal details in the stream.",[154,951,952,956],{},[19,953,955],{"href":954},"\u002Fnetwork-protocol-optimization\u002Fstreaming-ssr-and-early-flush\u002Fstreaming-html-through-cdns-and-proxies\u002F","Streaming HTML through CDNs and proxies"," — making sure the stream is not buffered.",[154,958,959,963],{},[19,960,962],{"href":961},"\u002Fcore-web-vitals-measurement\u002Freducing-cumulative-layout-shift-cls\u002Ffixing-cls-from-skeleton-screens-that-resize\u002F","Fixing CLS from skeleton screens that resize"," — reserving space for client fragments.",[965,966,968],"script",{"type":967},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Edge Side Includes vs Client-Side Fragments: Where to Assemble the Page\",\n  \"description\": \"Where to stitch personal or fast-changing fragments into a cached page — at the edge or in the browser — and what each choice does to Core Web Vitals.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Classify fragments by position and latency\",\n      \"text\": \"Fragments in the first viewport that affect layout (a personalised banner above the hero) favour edge assembly.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Stream edge-assembled fragments rather than blocking\",\n      \"text\": \"Expected outcome: shell bytes before the slot arrive at cache speed; only content after the slot waits.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Insert client fragments into reserved space\",\n      \"text\": \"Expected outcome: no layout shift and a fast, fragment-independent first paint.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Set timeouts for edge fragments\",\n      \"text\": \"Never let a slow fragment hold the page hostage.\"\n    }\n  ]\n}\n",[965,970,971],{"type":967},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"Edge Side Includes vs Client-Side Fragments: Where to Assemble the Page\",\n  \"description\": \"Where to stitch personal or fast-changing fragments into a cached page — at the edge or in the browser — and what each choice does to Core Web Vitals.\",\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\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fedge-side-includes-vs-client-side-fragments\u002F\"\n  }\n}\n",[965,973,974],{"type":967},"\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\": \"Advanced Caching Strategies & CDN Architecture\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"Edge Compute & Dynamic Caching\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Edge Side Includes vs Client-Side Fragments\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fedge-side-includes-vs-client-side-fragments\u002F\"\n    }\n  ]\n}\n",[976,977,978],"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 pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}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":358,"searchDepth":372,"depth":372,"links":980},[981,982,983,989,990,991,992,993,994],{"id":148,"depth":372,"text":149},{"id":181,"depth":372,"text":182},{"id":338,"depth":372,"text":339,"children":984},[985,986,987,988],{"id":343,"depth":437,"text":344},{"id":350,"depth":437,"text":351},{"id":532,"depth":437,"text":533},{"id":664,"depth":437,"text":665},{"id":802,"depth":372,"text":803},{"id":809,"depth":372,"text":810},{"id":816,"depth":372,"text":817},{"id":846,"depth":372,"text":847},{"id":874,"depth":372,"text":875},{"id":939,"depth":372,"text":940},"Where to stitch personal or fast-changing fragments into a cached page — at the edge or in the browser — and what each choice does to Core Web Vitals.","md",{"slug":998,"type":999,"breadcrumb":1000,"datePublished":1008,"dateModified":1008},"edge-side-includes-vs-client-side-fragments","article",[1001,1004,1005,1006],{"name":1002,"url":1003},"Home","\u002F",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1007},"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fedge-side-includes-vs-client-side-fragments\u002F","2026-10-06",true,"\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fedge-side-includes-vs-client-side-fragments",{"title":5,"description":1012},"Assemble cached pages from fragments at the edge (ESI or edge functions) or in the browser. Compare TTFB, LCP, CLS, caching and failure modes to choose.","advanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fedge-side-includes-vs-client-side-fragments\u002Findex","9o0OksQQr42OAnYYgsXMEMVF9PfJKO0L7VD7ioCgVPE",[1016,1020],{"title":1017,"path":1018,"stem":1019},"Caching HTML for Logged-In Users Safely","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fcaching-html-for-logged-in-users-safely","advanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fcaching-html-for-logged-in-users-safely\u002Findex",{"title":1021,"path":1022,"stem":1023},"Personalizing Cached Pages at the Edge","\u002Fadvanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fpersonalizing-cached-pages-at-the-edge","advanced-caching-strategies-cdn-architecture\u002Fedge-compute-and-dynamic-caching\u002Fpersonalizing-cached-pages-at-the-edge\u002Findex",1791308079608]