[{"data":1,"prerenderedAt":1863},["ShallowReactive",2],{"page:\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds":3,"all-docs-nav":1621},{"id":4,"title":5,"body":6,"breadcrumb":1593,"dateModified":1603,"datePublished":1603,"description":1604,"extension":1605,"faq":1606,"meta":1614,"navigation":1615,"path":1616,"seo":1617,"slug":12,"stem":1618,"type":1619,"__hash__":1620},"content\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds\u002Findex.md","Fixing CLS From Late-Loading Embeds",{"type":7,"value":8,"toc":1570},"minimark",[9,13,17,26,31,44,48,221,225,230,233,318,321,325,328,401,404,408,480,622,626,629,741,910,917,921,924,927,934,938,941,1142,1145,1231,1235,1238,1335,1424,1428,1470,1474,1481,1485,1489,1492,1496,1502,1506,1509,1513,1516,1520,1527,1531,1566],[10,11,5],"h1",{"id":12},"fixing-cls-from-late-loading-embeds",[14,15,16],"p",{},"Once images have dimensions and fonts stop reflowing, the remaining layout shift on a static site comes from things that were never in the build output: a comment thread fetched on scroll, a video player injected by a script, a map that hydrates into an empty div, a consent banner prepended to the body. All of them share one property — they take up space that was not reserved for them.",[14,18,19,20,25],{},"This guide covers the reservation patterns for each type of embed, the overlay pattern for banners, and the measured result on a documentation page carrying three of them. It is part of ",[21,22,24],"a",{"href":23},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002F","Cumulative Layout Shift Fixes for Static Sites",".",[27,28,30],"h2",{"id":29},"prerequisites","Prerequisites",[32,33,34,38,41],"ul",{},[35,36,37],"li",{},"A page where images and fonts are already stable, so the remaining score is attributable to embeds.",[35,39,40],{},"Chrome DevTools Performance panel, or the Layout Instability script from the parent guide, to name the shifted node.",[35,42,43],{},"The ability to change the markup around the embed — most third-party snippets can be wrapped even when they cannot be modified.",[27,45,47],{"id":46},"the-four-shapes-of-embed-shift","The Four Shapes of Embed Shift",[49,50,51,217],"figure",{},[52,53,60,61,60,65,60,69,60,76],"svg",{"viewBox":54,"role":55,"ariaLabelledBy":56,"xmlns":59},"0 0 760 320","img",[57,58],"emb-shapes-title","emb-shapes-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[62,63,64],"title",{"id":57},"Four ways an embed shifts the page",[66,67,68],"desc",{"id":58},"Four labelled panels. Prepended banner pushes the whole page down and scores 0.19. Mid-page widget with no reserved box scores 0.11. Iframe without an aspect ratio scores 0.08. A fixed overlay banner and a reserved box both score 0.00.",[70,71],"rect",{"x":72,"y":72,"width":73,"height":74,"fill":75},"0","760","320","#ffffff",[77,78,80,81,80,89,80,100,80,106,80,113,80,118,80,124,80,129,80,133,80,137,80,141,80,145,80,151,80,155,80,159,80,162,80,166,80,170,80,174,80,177,80,181,80,184,80,191,80,195,80,200,80,203,80,205,80,208,80,213,60],"g",{"style":79},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[82,83,88],"text",{"x":84,"y":85,"fill":86,"style":87},"380","26","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","Where the score comes from, by placement",[70,90],{"x":91,"y":92,"width":93,"height":94,"rx":95,"fill":96,"opacity":97,"stroke":98,"style":99},"40","52","160","180","10","#ff595e","0.14","#d83b41","stroke-width:1.5px",[82,101,105],{"x":102,"y":103,"fill":86,"style":104},"120","76","font-size:12px;font-weight:700;text-anchor:middle","Prepended banner",[70,107],{"x":108,"y":109,"width":110,"height":85,"rx":111,"fill":96,"opacity":112,"stroke":98,"style":99},"58","88","124","4","0.3",[82,114,117],{"x":102,"y":115,"fill":86,"style":116},"106","font-size:11px;text-anchor:middle","banner",[70,119],{"x":108,"y":120,"width":110,"height":121,"rx":111,"fill":122,"opacity":123},"122","80","#d9e2ef","0.6",[82,125,128],{"x":102,"y":126,"fill":127,"style":116},"168","#556071","whole page moves",[82,130,132],{"x":102,"y":131,"fill":86,"style":104},"222","CLS 0.19",[70,134],{"x":135,"y":92,"width":93,"height":94,"rx":95,"fill":96,"opacity":136,"stroke":98,"style":99},"220","0.10",[82,138,140],{"x":139,"y":103,"fill":86,"style":104},"300","Mid-page widget",[70,142],{"x":143,"y":109,"width":110,"height":144,"rx":111,"fill":122,"opacity":123},"238","34",[70,146],{"x":143,"y":147,"width":110,"height":91,"rx":111,"fill":148,"opacity":149,"stroke":150,"style":99},"130","#ffca3a","0.34","#a97b00",[82,152,154],{"x":139,"y":153,"fill":86,"style":116},"155","comments appear",[70,156],{"x":143,"y":157,"width":110,"height":158,"rx":111,"fill":122,"opacity":123},"178","24",[82,160,161],{"x":139,"y":131,"fill":86,"style":104},"CLS 0.11",[70,163],{"x":164,"y":92,"width":93,"height":94,"rx":95,"fill":148,"opacity":165,"stroke":150,"style":99},"400","0.18",[82,167,169],{"x":168,"y":103,"fill":86,"style":104},"480","Ratio-less iframe",[70,171],{"x":172,"y":109,"width":110,"height":173,"rx":111,"fill":148,"opacity":149,"stroke":150,"style":99},"418","60",[82,175,176],{"x":168,"y":120,"fill":86,"style":116},"grows to 315 px",[70,178],{"x":172,"y":179,"width":110,"height":180,"rx":111,"fill":122,"opacity":123},"156","46",[82,182,183],{"x":168,"y":131,"fill":86,"style":104},"CLS 0.08",[70,185],{"x":186,"y":92,"width":187,"height":94,"rx":95,"fill":188,"opacity":189,"stroke":190,"style":99},"580","140","#8ac926","0.16","#5a8a16",[82,192,194],{"x":193,"y":103,"fill":86,"style":104},"650","Reserved \u002F fixed",[70,196],{"x":197,"y":109,"width":198,"height":173,"rx":111,"fill":188,"opacity":199,"stroke":190,"style":99},"596","108","0.22",[82,201,202],{"x":193,"y":120,"fill":86,"style":116},"box waiting",[70,204],{"x":197,"y":179,"width":198,"height":180,"rx":111,"fill":122,"opacity":123},[82,206,207],{"x":193,"y":131,"fill":86,"style":104},"CLS 0.00",[82,209,212],{"x":91,"y":210,"fill":127,"style":211},"272","font-size:11px","Measured on one docs template, Chrome 4× CPU throttle · the placement matters more than the size",[82,214,216],{"x":91,"y":215,"fill":127,"style":211},"292","A small element inserted at the top outscores a large one appended at the bottom",[218,219,220],"figcaption",{},"Position dominates: an element inserted above existing content moves everything below it, which is why banners are worse than comment threads even though they are smaller.",[27,222,224],{"id":223},"the-recipe","The Recipe",[226,227,229],"h3",{"id":228},"_1-emit-the-container-at-build-time","1. Emit the container at build time",[14,231,232],{},"The generator already knows the widget will be there — the only unknown is its height. Emit the box during the build and let the script fill it:",[234,235,240],"pre",{"className":236,"code":237,"language":238,"meta":239,"style":239},"language-html shiki shiki-themes github-light github-dark","\u003Csection class=\"comments\" data-comments=\"post-42\" style=\"min-height:480px\">\n  \u003Cp class=\"comments-loading\">Loading discussion…\u003C\u002Fp>\n\u003C\u002Fsection>\n","html","",[241,242,243,286,308],"code",{"__ignoreMap":239},[244,245,248,252,256,260,263,267,270,272,275,278,280,283],"span",{"class":246,"line":247},"line",1,[244,249,251],{"class":250},"sVt8B","\u003C",[244,253,255],{"class":254},"s9eBZ","section",[244,257,259],{"class":258},"sScJk"," class",[244,261,262],{"class":250},"=",[244,264,266],{"class":265},"sZZnC","\"comments\"",[244,268,269],{"class":258}," data-comments",[244,271,262],{"class":250},[244,273,274],{"class":265},"\"post-42\"",[244,276,277],{"class":258}," style",[244,279,262],{"class":250},[244,281,282],{"class":265},"\"min-height:480px\"",[244,284,285],{"class":250},">\n",[244,287,289,292,294,296,298,301,304,306],{"class":246,"line":288},2,[244,290,291],{"class":250},"  \u003C",[244,293,14],{"class":254},[244,295,259],{"class":258},[244,297,262],{"class":250},[244,299,300],{"class":265},"\"comments-loading\"",[244,302,303],{"class":250},">Loading discussion…\u003C\u002F",[244,305,14],{"class":254},[244,307,285],{"class":250},[244,309,311,314,316],{"class":246,"line":310},3,[244,312,313],{"class":250},"\u003C\u002F",[244,315,255],{"class":254},[244,317,285],{"class":250},[14,319,320],{},"The placeholder text is not decoration: it keeps the box from looking broken while the fetch runs, and it gives readers on failed loads something honest to read.",[226,322,324],{"id":323},"_2-take-overlays-out-of-flow","2. Take overlays out of flow",[14,326,327],{},"Anything that is not part of the article should not be in the article's flow:",[234,329,333],{"className":330,"code":331,"language":332,"meta":239,"style":239},"language-css shiki shiki-themes github-light github-dark",".consent {\n  position: fixed;\n  inset: auto 0 0 0;     \u002F* pinned to the bottom, over the page *\u002F\n  z-index: 50;\n}\n","css",[241,334,335,343,358,382,395],{"__ignoreMap":239},[244,336,337,340],{"class":246,"line":247},[244,338,339],{"class":258},".consent",[244,341,342],{"class":250}," {\n",[244,344,345,349,352,355],{"class":246,"line":288},[244,346,348],{"class":347},"sj4cs","  position",[244,350,351],{"class":250},": ",[244,353,354],{"class":347},"fixed",[244,356,357],{"class":250},";\n",[244,359,360,363,365,368,371,373,375,378],{"class":246,"line":310},[244,361,362],{"class":347},"  inset",[244,364,351],{"class":250},[244,366,367],{"class":347},"auto",[244,369,370],{"class":347}," 0",[244,372,370],{"class":347},[244,374,370],{"class":347},[244,376,377],{"class":250},";     ",[244,379,381],{"class":380},"sJ8bj","\u002F* pinned to the bottom, over the page *\u002F\n",[244,383,385,388,390,393],{"class":246,"line":384},4,[244,386,387],{"class":347},"  z-index",[244,389,351],{"class":250},[244,391,392],{"class":347},"50",[244,394,357],{"class":250},[244,396,398],{"class":246,"line":397},5,[244,399,400],{"class":250},"}\n",[14,402,403],{},"A fixed element participates in no layout calculation, so it can appear at any moment for free. This one change is usually the largest single CLS win on a site with a consent banner.",[226,405,407],{"id":406},"_3-give-iframes-a-ratio-always","3. Give iframes a ratio, always",[234,409,411],{"className":236,"code":410,"language":238,"meta":239,"style":239},"\u003Cdiv class=\"embed embed-video\">\n  \u003Ciframe src=\"https:\u002F\u002Fplayer.example.com\u002Fv\u002Fabc\" title=\"Deploy walkthrough\"\n          loading=\"lazy\" allowfullscreen>\u003C\u002Fiframe>\n\u003C\u002Fdiv>\n",[241,412,413,429,452,472],{"__ignoreMap":239},[244,414,415,417,420,422,424,427],{"class":246,"line":247},[244,416,251],{"class":250},[244,418,419],{"class":254},"div",[244,421,259],{"class":258},[244,423,262],{"class":250},[244,425,426],{"class":265},"\"embed embed-video\"",[244,428,285],{"class":250},[244,430,431,433,436,439,441,444,447,449],{"class":246,"line":288},[244,432,291],{"class":250},[244,434,435],{"class":254},"iframe",[244,437,438],{"class":258}," src",[244,440,262],{"class":250},[244,442,443],{"class":265},"\"https:\u002F\u002Fplayer.example.com\u002Fv\u002Fabc\"",[244,445,446],{"class":258}," title",[244,448,262],{"class":250},[244,450,451],{"class":265},"\"Deploy walkthrough\"\n",[244,453,454,457,459,462,465,468,470],{"class":246,"line":310},[244,455,456],{"class":258},"          loading",[244,458,262],{"class":250},[244,460,461],{"class":265},"\"lazy\"",[244,463,464],{"class":258}," allowfullscreen",[244,466,467],{"class":250},">\u003C\u002F",[244,469,435],{"class":254},[244,471,285],{"class":250},[244,473,474,476,478],{"class":246,"line":384},[244,475,313],{"class":250},[244,477,419],{"class":254},[244,479,285],{"class":250},[234,481,483],{"className":330,"code":482,"language":332,"meta":239,"style":239},".embed { position: relative; width: 100%; }\n.embed-video { aspect-ratio: 16 \u002F 9; }\n.embed-map { aspect-ratio: 4 \u002F 3; }\n.embed > iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }\n",[241,484,485,519,542,562],{"__ignoreMap":239},[244,486,487,490,493,496,498,501,504,507,509,512,516],{"class":246,"line":247},[244,488,489],{"class":258},".embed",[244,491,492],{"class":250}," { ",[244,494,495],{"class":347},"position",[244,497,351],{"class":250},[244,499,500],{"class":347},"relative",[244,502,503],{"class":250},"; ",[244,505,506],{"class":347},"width",[244,508,351],{"class":250},[244,510,511],{"class":347},"100",[244,513,515],{"class":514},"szBVR","%",[244,517,518],{"class":250},"; }\n",[244,520,521,524,526,529,531,534,537,540],{"class":246,"line":288},[244,522,523],{"class":258},".embed-video",[244,525,492],{"class":250},[244,527,528],{"class":347},"aspect-ratio",[244,530,351],{"class":250},[244,532,533],{"class":347},"16",[244,535,536],{"class":250}," \u002F ",[244,538,539],{"class":347},"9",[244,541,518],{"class":250},[244,543,544,547,549,551,553,555,557,560],{"class":246,"line":310},[244,545,546],{"class":258},".embed-map",[244,548,492],{"class":250},[244,550,528],{"class":347},[244,552,351],{"class":250},[244,554,111],{"class":347},[244,556,536],{"class":250},[244,558,559],{"class":347},"3",[244,561,518],{"class":250},[244,563,564,566,569,572,574,576,578,581,583,586,588,590,592,594,596,598,600,602,605,607,609,611,613,616,618,620],{"class":246,"line":384},[244,565,489],{"class":258},[244,567,568],{"class":514}," >",[244,570,571],{"class":254}," iframe",[244,573,492],{"class":250},[244,575,495],{"class":347},[244,577,351],{"class":250},[244,579,580],{"class":347},"absolute",[244,582,503],{"class":250},[244,584,585],{"class":347},"inset",[244,587,351],{"class":250},[244,589,72],{"class":347},[244,591,503],{"class":250},[244,593,506],{"class":347},[244,595,351],{"class":250},[244,597,511],{"class":347},[244,599,515],{"class":514},[244,601,503],{"class":250},[244,603,604],{"class":347},"height",[244,606,351],{"class":250},[244,608,511],{"class":347},[244,610,515],{"class":514},[244,612,503],{"class":250},[244,614,615],{"class":347},"border",[244,617,351],{"class":250},[244,619,72],{"class":347},[244,621,518],{"class":250},[226,623,625],{"id":624},"_4-prefer-click-to-load-for-heavy-players","4. Prefer click-to-load for heavy players",[14,627,628],{},"A third-party video player is typically 400-900 KB of JavaScript. Replacing it with a poster image and a play button gives you a naturally fixed box, removes the payload from the initial load, and only pays the cost for readers who press play:",[234,630,632],{"className":236,"code":631,"language":238,"meta":239,"style":239},"\u003Cbutton class=\"video-facade\" data-video=\"abc\" aria-label=\"Play: Deploy walkthrough\">\n  \u003Cimg src=\"\u002Fimg\u002Fposter-abc.avif\" width=\"1280\" height=\"720\" alt=\"\">\n  \u003Cspan class=\"video-facade-play\" aria-hidden=\"true\">▶\u003C\u002Fspan>\n\u003C\u002Fbutton>\n",[241,633,634,666,705,733],{"__ignoreMap":239},[244,635,636,638,641,643,645,648,651,653,656,659,661,664],{"class":246,"line":247},[244,637,251],{"class":250},[244,639,640],{"class":254},"button",[244,642,259],{"class":258},[244,644,262],{"class":250},[244,646,647],{"class":265},"\"video-facade\"",[244,649,650],{"class":258}," data-video",[244,652,262],{"class":250},[244,654,655],{"class":265},"\"abc\"",[244,657,658],{"class":258}," aria-label",[244,660,262],{"class":250},[244,662,663],{"class":265},"\"Play: Deploy walkthrough\"",[244,665,285],{"class":250},[244,667,668,670,672,674,676,679,682,684,687,690,692,695,698,700,703],{"class":246,"line":288},[244,669,291],{"class":250},[244,671,55],{"class":254},[244,673,438],{"class":258},[244,675,262],{"class":250},[244,677,678],{"class":265},"\"\u002Fimg\u002Fposter-abc.avif\"",[244,680,681],{"class":258}," width",[244,683,262],{"class":250},[244,685,686],{"class":265},"\"1280\"",[244,688,689],{"class":258}," height",[244,691,262],{"class":250},[244,693,694],{"class":265},"\"720\"",[244,696,697],{"class":258}," alt",[244,699,262],{"class":250},[244,701,702],{"class":265},"\"\"",[244,704,285],{"class":250},[244,706,707,709,711,713,715,718,721,723,726,729,731],{"class":246,"line":310},[244,708,291],{"class":250},[244,710,244],{"class":254},[244,712,259],{"class":258},[244,714,262],{"class":250},[244,716,717],{"class":265},"\"video-facade-play\"",[244,719,720],{"class":258}," aria-hidden",[244,722,262],{"class":250},[244,724,725],{"class":265},"\"true\"",[244,727,728],{"class":250},">▶\u003C\u002F",[244,730,244],{"class":254},[244,732,285],{"class":250},[244,734,735,737,739],{"class":246,"line":384},[244,736,313],{"class":250},[244,738,640],{"class":254},[244,740,285],{"class":250},[234,742,746],{"className":743,"code":744,"language":745,"meta":239,"style":239},"language-javascript shiki shiki-themes github-light github-dark","document.querySelectorAll('.video-facade').forEach((el) => {\n  el.addEventListener('click', () => {\n    const frame = document.createElement('iframe');\n    frame.src = `https:\u002F\u002Fplayer.example.com\u002Fv\u002F${el.dataset.video}?autoplay=1`;\n    frame.title = 'Deploy walkthrough';\n    frame.allow = 'autoplay; fullscreen';\n    el.replaceWith(frame);\n  }, { once: true });\n});\n","javascript",[241,747,748,783,803,828,855,867,880,892,904],{"__ignoreMap":239},[244,749,750,753,756,759,762,765,768,771,775,778,781],{"class":246,"line":247},[244,751,752],{"class":250},"document.",[244,754,755],{"class":258},"querySelectorAll",[244,757,758],{"class":250},"(",[244,760,761],{"class":265},"'.video-facade'",[244,763,764],{"class":250},").",[244,766,767],{"class":258},"forEach",[244,769,770],{"class":250},"((",[244,772,774],{"class":773},"s4XuR","el",[244,776,777],{"class":250},") ",[244,779,780],{"class":514},"=>",[244,782,342],{"class":250},[244,784,785,788,791,793,796,799,801],{"class":246,"line":288},[244,786,787],{"class":250},"  el.",[244,789,790],{"class":258},"addEventListener",[244,792,758],{"class":250},[244,794,795],{"class":265},"'click'",[244,797,798],{"class":250},", () ",[244,800,780],{"class":514},[244,802,342],{"class":250},[244,804,805,808,811,814,817,820,822,825],{"class":246,"line":310},[244,806,807],{"class":514},"    const",[244,809,810],{"class":347}," frame",[244,812,813],{"class":514}," =",[244,815,816],{"class":250}," document.",[244,818,819],{"class":258},"createElement",[244,821,758],{"class":250},[244,823,824],{"class":265},"'iframe'",[244,826,827],{"class":250},");\n",[244,829,830,833,835,838,840,842,845,847,850,853],{"class":246,"line":384},[244,831,832],{"class":250},"    frame.src ",[244,834,262],{"class":514},[244,836,837],{"class":265}," `https:\u002F\u002Fplayer.example.com\u002Fv\u002F${",[244,839,774],{"class":250},[244,841,25],{"class":265},[244,843,844],{"class":250},"dataset",[244,846,25],{"class":265},[244,848,849],{"class":250},"video",[244,851,852],{"class":265},"}?autoplay=1`",[244,854,357],{"class":250},[244,856,857,860,862,865],{"class":246,"line":397},[244,858,859],{"class":250},"    frame.title ",[244,861,262],{"class":514},[244,863,864],{"class":265}," 'Deploy walkthrough'",[244,866,357],{"class":250},[244,868,870,873,875,878],{"class":246,"line":869},6,[244,871,872],{"class":250},"    frame.allow ",[244,874,262],{"class":514},[244,876,877],{"class":265}," 'autoplay; fullscreen'",[244,879,357],{"class":250},[244,881,883,886,889],{"class":246,"line":882},7,[244,884,885],{"class":250},"    el.",[244,887,888],{"class":258},"replaceWith",[244,890,891],{"class":250},"(frame);\n",[244,893,895,898,901],{"class":246,"line":894},8,[244,896,897],{"class":250},"  }, { once: ",[244,899,900],{"class":347},"true",[244,902,903],{"class":250}," });\n",[244,905,907],{"class":246,"line":906},9,[244,908,909],{"class":250},"});\n",[14,911,912,913,25],{},"Because the replacement happens inside 500 ms of a click, the swap is inside the user-interaction exclusion window and costs nothing even though the sizes differ slightly. The byte savings are covered further in ",[21,914,916],{"href":915},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Flazy-loading-youtube-embeds-on-static-sites\u002F","Lazy-Loading YouTube Embeds on Static Sites",[226,918,920],{"id":919},"_5-never-insert-above-existing-content","5. Never insert above existing content",[14,922,923],{},"If content must arrive late and cannot be reserved — a live status strip, a personalised recommendation row — append it below the fold or render it into a fixed overlay. Prepending is the single most expensive thing a script can do to a page's layout.",[14,925,926],{},"There is one exception worth knowing: an element inserted above content that has not painted yet costs nothing, because nothing moved. That is why server-rendering the banner into the build output is strictly better than injecting it, even when the banner itself is unavoidable. If the decision of whether to show it depends on a cookie, render it hidden and reveal it with a class toggle — a visibility change on an already-reserved box is not a layout shift.",[14,928,929,930,25],{},"The same reasoning applies to A\u002FB tests and personalisation. Swapping a hero's text after paint shifts everything below it; swapping it at the edge, before the HTML reaches the browser, shifts nothing. When a static site has an edge layer in front of it, that is usually the cheapest place to solve the whole class of problem — the pattern is sketched in ",[21,931,933],{"href":932},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002F","Cloudflare Pages Edge Caching Setup",[27,935,937],{"id":936},"inventory-before-you-fix","Inventory Before You Fix",[14,939,940],{},"Most teams underestimate how many late-arriving elements a page carries, because each was added by a different person for a different reason. Take a census before touching anything: load the page with a paused debugger, then diff the served HTML against the live DOM.",[234,942,944],{"className":743,"code":943,"language":745,"meta":239,"style":239},"\u002F\u002F Paste in the console after the page settles — lists nodes not present in the HTML response\nconst served = await fetch(location.href).then((r) => r.text());\nconst shipped = new DOMParser().parseFromString(served, 'text\u002Fhtml');\nconst inHtml = new Set([...shipped.querySelectorAll('[class]')].map((el) => el.className));\n[...document.querySelectorAll('body [class]')]\n  .filter((el) => !inHtml.has(el.className))\n  .forEach((el) => console.log(el.tagName, el.className, el.getBoundingClientRect().height));\n",[241,945,946,951,990,1019,1066,1085,1113],{"__ignoreMap":239},[244,947,948],{"class":246,"line":247},[244,949,950],{"class":380},"\u002F\u002F Paste in the console after the page settles — lists nodes not present in the HTML response\n",[244,952,953,956,959,961,964,967,970,973,975,978,980,982,985,987],{"class":246,"line":288},[244,954,955],{"class":514},"const",[244,957,958],{"class":347}," served",[244,960,813],{"class":514},[244,962,963],{"class":514}," await",[244,965,966],{"class":258}," fetch",[244,968,969],{"class":250},"(location.href).",[244,971,972],{"class":258},"then",[244,974,770],{"class":250},[244,976,977],{"class":773},"r",[244,979,777],{"class":250},[244,981,780],{"class":514},[244,983,984],{"class":250}," r.",[244,986,82],{"class":258},[244,988,989],{"class":250},"());\n",[244,991,992,994,997,999,1002,1005,1008,1011,1014,1017],{"class":246,"line":310},[244,993,955],{"class":514},[244,995,996],{"class":347}," shipped",[244,998,813],{"class":514},[244,1000,1001],{"class":514}," new",[244,1003,1004],{"class":258}," DOMParser",[244,1006,1007],{"class":250},"().",[244,1009,1010],{"class":258},"parseFromString",[244,1012,1013],{"class":250},"(served, ",[244,1015,1016],{"class":265},"'text\u002Fhtml'",[244,1018,827],{"class":250},[244,1020,1021,1023,1026,1028,1030,1033,1036,1039,1042,1044,1046,1049,1052,1055,1057,1059,1061,1063],{"class":246,"line":384},[244,1022,955],{"class":514},[244,1024,1025],{"class":347}," inHtml",[244,1027,813],{"class":514},[244,1029,1001],{"class":514},[244,1031,1032],{"class":258}," Set",[244,1034,1035],{"class":250},"([",[244,1037,1038],{"class":514},"...",[244,1040,1041],{"class":250},"shipped.",[244,1043,755],{"class":258},[244,1045,758],{"class":250},[244,1047,1048],{"class":265},"'[class]'",[244,1050,1051],{"class":250},")].",[244,1053,1054],{"class":258},"map",[244,1056,770],{"class":250},[244,1058,774],{"class":773},[244,1060,777],{"class":250},[244,1062,780],{"class":514},[244,1064,1065],{"class":250}," el.className));\n",[244,1067,1068,1071,1073,1075,1077,1079,1082],{"class":246,"line":397},[244,1069,1070],{"class":250},"[",[244,1072,1038],{"class":514},[244,1074,752],{"class":250},[244,1076,755],{"class":258},[244,1078,758],{"class":250},[244,1080,1081],{"class":265},"'body [class]'",[244,1083,1084],{"class":250},")]\n",[244,1086,1087,1090,1093,1095,1097,1099,1101,1104,1107,1110],{"class":246,"line":869},[244,1088,1089],{"class":250},"  .",[244,1091,1092],{"class":258},"filter",[244,1094,770],{"class":250},[244,1096,774],{"class":773},[244,1098,777],{"class":250},[244,1100,780],{"class":514},[244,1102,1103],{"class":514}," !",[244,1105,1106],{"class":250},"inHtml.",[244,1108,1109],{"class":258},"has",[244,1111,1112],{"class":250},"(el.className))\n",[244,1114,1115,1117,1119,1121,1123,1125,1127,1130,1133,1136,1139],{"class":246,"line":882},[244,1116,1089],{"class":250},[244,1118,767],{"class":258},[244,1120,770],{"class":250},[244,1122,774],{"class":773},[244,1124,777],{"class":250},[244,1126,780],{"class":514},[244,1128,1129],{"class":250}," console.",[244,1131,1132],{"class":258},"log",[244,1134,1135],{"class":250},"(el.tagName, el.className, el.",[244,1137,1138],{"class":258},"getBoundingClientRect",[244,1140,1141],{"class":250},"().height));\n",[14,1143,1144],{},"Each row is an element the build did not produce, together with the height it eventually occupies — which is exactly the number to reserve. Sort by height descending and fix from the top; on the site measured below, four of the eleven rows accounted for the entire score.",[49,1146,1147,1228],{},[52,1148,60,1153,60,1156,60,1159,60,1165],{"viewBox":1149,"role":55,"ariaLabelledBy":1150,"xmlns":59},"18 -13 538 285",[1151,1152],"emb-inv-title","emb-inv-desc",[62,1154,1155],{"id":1151},"Census of elements added after the HTML response",[66,1157,1158],{"id":1152},"A ranked list of late-added elements with their eventual heights: consent banner 96 pixels prepended, comment thread 520 pixels mid-page, video player 315 pixels mid-page, newsletter form 180 pixels appended, and six smaller elements under 40 pixels each. Only the first four are marked as worth reserving.",[70,1160],{"x":1161,"y":1162,"width":1163,"height":1164,"fill":75},"18","-13","538","285",[77,1166,80,1167,80,1170,80,1175,80,1180,80,1185,80,1189,80,1193,80,1196,80,1200,80,1204,80,1207,80,1210,80,1214,80,1216,80,1219,80,1224,60],{"style":79},[82,1168,1169],{"x":84,"y":85,"fill":86,"style":87},"Eleven late elements, four that matter",[70,1171],{"x":91,"y":1172,"width":1173,"height":144,"rx":1174,"fill":96,"opacity":189,"stroke":98,"style":99},"48","330","6",[82,1176,1179],{"x":1177,"y":1178,"fill":86,"style":104},"205","70",".consent · 96 px · prepended",[82,1181,1184],{"x":1182,"y":1178,"fill":98,"style":1183},"386","font-size:12px;font-weight:700","reserve or overlay",[70,1186],{"x":91,"y":1187,"width":1173,"height":144,"rx":1174,"fill":96,"opacity":1188,"stroke":98,"style":99},"92","0.13",[82,1190,1192],{"x":1177,"y":1191,"fill":86,"style":104},"114",".comments · 520 px · mid-page",[82,1194,1195],{"x":1182,"y":1191,"fill":98,"style":1183},"reserve",[70,1197],{"x":91,"y":1198,"width":1173,"height":144,"rx":1174,"fill":148,"opacity":1199,"stroke":150,"style":99},"136","0.32",[82,1201,1203],{"x":1177,"y":1202,"fill":86,"style":104},"158","iframe.player · 315 px · mid-page",[82,1205,1206],{"x":1182,"y":1202,"fill":150,"style":1183},"ratio wrapper",[70,1208],{"x":91,"y":94,"width":1173,"height":144,"rx":1174,"fill":148,"opacity":1209,"stroke":150,"style":99},"0.24",[82,1211,1213],{"x":1177,"y":1212,"fill":86,"style":104},"202",".newsletter · 180 px · appended",[82,1215,1195],{"x":1182,"y":1212,"fill":150,"style":1183},[70,1217],{"x":91,"y":1218,"width":1173,"height":85,"rx":1174,"fill":122,"opacity":123,"stroke":127,"style":99},"224",[82,1220,1223],{"x":1177,"y":1221,"fill":86,"style":1222},"242","font-size:12px;text-anchor:middle","6 more elements, all under 40 px",[82,1225,1227],{"x":1182,"y":1221,"fill":127,"style":1226},"font-size:12px","ignore for now",[218,1229,1230],{},"Ranking by eventual height turns a vague \"the page is jumpy\" into a four-item task list — and tells you the exact `min-height` to reserve for each.",[27,1232,1234],{"id":1233},"measured-impact","Measured Impact",[14,1236,1237],{},"A documentation article carrying a consent banner, a comment thread and one video embed, Chrome 4× CPU throttle on a fast 3G profile, median of five runs:",[1239,1240,1241,1260],"table",{},[1242,1243,1244],"thead",{},[1245,1246,1247,1251,1254,1257],"tr",{},[1248,1249,1250],"th",{},"Change",[1248,1252,1253],{},"CLS",[1248,1255,1256],{},"JS transferred",[1248,1258,1259],{},"Notes",[1261,1262,1263,1278,1291,1307,1322],"tbody",{},[1245,1264,1265,1269,1272,1275],{},[1266,1267,1268],"td",{},"Baseline: all three in flow, unreserved",[1266,1270,1271],{},"0.38",[1266,1273,1274],{},"940 KB",[1266,1276,1277],{},"Banner prepend dominates the burst",[1245,1279,1280,1283,1286,1288],{},[1266,1281,1282],{},"Banner as fixed overlay",[1266,1284,1285],{},"0.19",[1266,1287,1274],{},[1266,1289,1290],{},"Removes the whole-page push",[1245,1292,1293,1299,1302,1304],{},[1266,1294,1295,1298],{},[241,1296,1297],{},"min-height"," on the comment container",[1266,1300,1301],{},"0.08",[1266,1303,1274],{},[1266,1305,1306],{},"Residual is the player growing",[1245,1308,1309,1314,1317,1319],{},[1266,1310,1311,1313],{},[241,1312,528],{}," wrapper on the iframe",[1266,1315,1316],{},"0.01",[1266,1318,1274],{},[1266,1320,1321],{},"All three boxes now reserved",[1245,1323,1324,1327,1329,1332],{},[1266,1325,1326],{},"Video facade instead of the player",[1266,1328,1316],{},[1266,1330,1331],{},"210 KB",[1266,1333,1334],{},"730 KB removed from the initial load",[49,1336,1337,1421],{},[52,1338,60,1343,60,1346,60,1349,60,1352],{"viewBox":1339,"role":55,"ariaLabelledBy":1340,"xmlns":59},"0 0 760 280",[1341,1342],"emb-steps-title","emb-steps-desc",[62,1344,1345],{"id":1341},"CLS and JavaScript weight across five changes",[66,1347,1348],{"id":1342},"A step chart with two series. The CLS line falls from 0.38 to 0.19 after the banner overlay, 0.08 after reserving the comment box, and 0.01 after the iframe wrapper, then stays flat. The JavaScript line stays at 940 kilobytes until the final change, where the video facade drops it to 210 kilobytes.",[70,1350],{"x":72,"y":72,"width":73,"height":1351,"fill":75},"280",[77,1353,80,1354,80,1357,80,1362,80,1365,80,1371,80,1376,80,1380,80,1384,80,1387,80,1391,80,1395,80,1399,80,1402,80,1406,80,1410,80,1414,80,1417,60],{"style":79},[82,1355,1356],{"x":84,"y":85,"fill":86,"style":87},"Layout and payload are separate wins",[246,1358],{"x1":1359,"y1":1360,"x2":1359,"y2":1361,"stroke":127,"style":99},"90","56","200",[246,1363],{"x1":1359,"y1":1361,"x2":1364,"y2":1361,"stroke":127,"style":99},"700",[1366,1367],"polyline",{"points":1368,"fill":1369,"stroke":98,"style":1370},"130,66 260,138 390,170 520,194 650,194","none","stroke-width:2.5px",[1366,1372],{"points":1373,"fill":1369,"stroke":1374,"style":1375},"130,86 260,86 390,86 520,86 650,178","#1982c4","stroke-width:2.5px;stroke-dasharray:7 5",[82,1377,1379],{"x":187,"y":173,"fill":98,"style":1378},"font-size:11px;font-weight:700","CLS 0.38",[82,1381,1285],{"x":1382,"y":1383,"fill":98,"style":1378},"270","132",[82,1385,1301],{"x":164,"y":1386,"fill":150,"style":1378},"164",[82,1388,1316],{"x":1389,"y":1390,"fill":190,"style":1378},"530","188",[82,1392,1394],{"x":187,"y":121,"fill":1393,"style":1378},"#166a9e","JS 940 KB",[82,1396,1331],{"x":1397,"y":1398,"fill":1393,"style":1378},"600","172",[82,1400,1401],{"x":147,"y":135,"fill":127,"style":116},"baseline",[82,1403,1405],{"x":1404,"y":135,"fill":127,"style":116},"260","overlay",[82,1407,1409],{"x":1408,"y":135,"fill":127,"style":116},"390","comment box",[82,1411,1413],{"x":1412,"y":135,"fill":127,"style":116},"520","iframe ratio",[82,1415,1416],{"x":193,"y":135,"fill":127,"style":116},"video facade",[82,1418,1420],{"x":1359,"y":1419,"fill":127,"style":211},"252","Solid line: CLS (lower is better) · Dashed line: JavaScript transferred on first load",[218,1422,1423],{},"The first three changes are pure layout work and cost no bytes; the facade is the only one that also removes payload — which is why it is worth doing even after the score is already green.",[27,1425,1427],{"id":1426},"pitfalls-rollback","Pitfalls & Rollback",[32,1429,1430,1437,1446,1452,1458,1464],{},[35,1431,1432,1436],{},[1433,1434,1435],"strong",{},"Reserving too little."," An under-sized box still shifts, just less. Measure the real rendered height in production and reserve at the median, not the minimum.",[35,1438,1439,1442,1443,1445],{},[1433,1440,1441],{},"Reserving with JavaScript."," Setting ",[241,1444,1297],{}," from a script runs after paint and reserves nothing. It must be in the HTML or the stylesheet.",[35,1447,1448,1451],{},[1433,1449,1450],{},"Assuming lazy loading is a fix."," Deferring moves the shift to scroll time, where it still counts. Reserve first, then lazy-load.",[35,1453,1454,1457],{},[1433,1455,1456],{},"Overlay without a scroll lock."," A fixed banner covering content is fine for CLS but can trap readers on small screens; give it a dismiss control and keep it short.",[35,1459,1460,1463],{},[1433,1461,1462],{},"Third-party heights that change."," Ad slots and status widgets change size across releases. Re-measure quarterly, or use a container with a fixed ratio the vendor guarantees.",[35,1465,1466,1469],{},[1433,1467,1468],{},"Rollback:"," every change is a wrapper element or a CSS rule. Removing the class restores the previous rendering; the facade pattern falls back to a plain link if its script never loads.",[27,1471,1473],{"id":1472},"conclusion","Conclusion",[14,1475,1476,1477,25],{},"Embeds shift pages because nothing reserved their space, and a static generator is in the perfect position to reserve it — the widget's existence is known at build time even when its height is not. Emit the container, wrap iframes in a ratio box, put banners in a fixed overlay, and use facades for heavy players. Then confirm with field data, since a lab run rarely triggers the scroll-loaded ones: see ",[21,1478,1480],{"href":1479},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Fmeasuring-cls-in-the-field-with-web-vitals-js\u002F","Measuring CLS in the Field With web-vitals.js",[27,1482,1484],{"id":1483},"faq","FAQ",[226,1486,1488],{"id":1487},"why-does-a-consent-banner-hurt-cls-so-much","Why does a consent banner hurt CLS so much?",[14,1490,1491],{},"Because it is usually prepended to the body, so it pushes the entire page down by its own height. The impact fraction is close to the whole viewport and the distance fraction is large, which is the worst possible combination. Rendering it as a fixed overlay removes it from the document flow and scores zero.",[226,1493,1495],{"id":1494},"how-do-i-reserve-space-for-a-comment-thread-whose-height-varies","How do I reserve space for a comment thread whose height varies?",[14,1497,1498,1499,1501],{},"Use a ",[241,1500,1297],{}," set to the median rendered height you observe in production. A thread that opens at 520 pixels in a container reserved for 480 shifts the page by 40 pixels rather than 520, which is usually the difference between passing and failing.",[226,1503,1505],{"id":1504},"does-intersectionobserver-lazy-loading-avoid-the-shift","Does IntersectionObserver lazy loading avoid the shift?",[14,1507,1508],{},"No. Deferring the load only moves the shift later, to the moment the reader scrolls the element into view, and shifts in the visible viewport count fully. Lazy loading is a bytes optimisation; the reservation is the layout fix and you need both.",[226,1510,1512],{"id":1511},"should-i-replace-a-video-embed-with-a-click-to-load-placeholder","Should I replace a video embed with a click-to-load placeholder?",[14,1514,1515],{},"Usually yes for third-party players. A static poster image with a play button gives you a fixed box, removes hundreds of kilobytes of player JavaScript from the initial load and only pays that cost for readers who actually watch.",[226,1517,1519],{"id":1518},"how-do-i-know-which-embed-caused-a-field-cls-score","How do I know which embed caused a field CLS score?",[14,1521,1522,1523,1526],{},"Record the layout-shift entries with the ",[241,1524,1525],{},"web-vitals"," attribution build, which reports the largest shifted element for each entry. That tells you the node rather than the page, which is what you need to fix it.",[27,1528,1530],{"id":1529},"related","Related",[32,1532,1533,1542,1549,1554,1561],{},[35,1534,1535,1538,1539,1541],{},[1433,1536,1537],{},"Parent:"," ",[21,1540,24],{"href":23}," — the full set of causes.",[35,1543,1544,1548],{},[21,1545,1547],{"href":1546},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift\u002F","Reserving Space for Images and Embeds"," — the dimension recipe these patterns build on.",[35,1550,1551,1553],{},[21,1552,916],{"href":915}," — the facade pattern in full, from the payload angle.",[35,1555,1556,1560],{},[21,1557,1559],{"href":1558},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002F","Third-Party Script Performance on Static Sites"," — budgets for everything a third party ships.",[35,1562,1563,1565],{},[21,1564,1480],{"href":1479}," — attributing a score to a specific node.",[1567,1568,1569],"style",{},"html pre.shiki code .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .s9eBZ, html code.shiki .s9eBZ{--shiki-default:#22863A;--shiki-dark:#85E89D}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}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 pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}",{"title":239,"searchDepth":288,"depth":288,"links":1571},[1572,1573,1574,1581,1582,1583,1584,1585,1592],{"id":29,"depth":288,"text":30},{"id":46,"depth":288,"text":47},{"id":223,"depth":288,"text":224,"children":1575},[1576,1577,1578,1579,1580],{"id":228,"depth":310,"text":229},{"id":323,"depth":310,"text":324},{"id":406,"depth":310,"text":407},{"id":624,"depth":310,"text":625},{"id":919,"depth":310,"text":920},{"id":936,"depth":288,"text":937},{"id":1233,"depth":288,"text":1234},{"id":1426,"depth":288,"text":1427},{"id":1472,"depth":288,"text":1473},{"id":1483,"depth":288,"text":1484,"children":1586},[1587,1588,1589,1590,1591],{"id":1487,"depth":310,"text":1488},{"id":1494,"depth":310,"text":1495},{"id":1504,"depth":310,"text":1505},{"id":1511,"depth":310,"text":1512},{"id":1518,"depth":310,"text":1519},{"id":1529,"depth":288,"text":1530},[1594,1597,1600,1601],{"name":1595,"item":1596},"Home","\u002F",{"name":1598,"item":1599},"Performance Optimization & Core Web Vitals for SSGs","\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002F",{"name":24,"item":23},{"name":5,"item":1602},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds\u002F","2026-08-01","Comment threads, video players, maps and consent banners land after paint and push the page around. Reserve their space at build time and take overlays out of flow.","md",[1607,1608,1610,1611,1612],{"q":1488,"a":1491},{"q":1495,"a":1609},"Use a min-height set to the median rendered height you observe in production. A thread that opens at 520 pixels in a container reserved for 480 shifts the page by 40 pixels rather than 520, which is usually the difference between passing and failing.",{"q":1505,"a":1508},{"q":1512,"a":1515},{"q":1519,"a":1613},"Record the layout-shift entries with the web-vitals attribution build, which reports the largest shifted element for each entry. That tells you the node rather than the page, which is what you need to fix it.",{},true,"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds",{"title":5,"description":1604},"performance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds\u002Findex","article","OoRQQPfitRlw22u6kAL8SYNCGnj5LuLjrv5kaKfcU5k",[1622,1625,1628,1631,1634,1637,1640,1643,1646,1649,1652,1655,1658,1661,1664,1667,1670,1673,1676,1679,1682,1685,1688,1691,1694,1697,1700,1703,1706,1709,1712,1715,1716,1718,1720,1722,1725,1728,1731,1734,1737,1740,1743,1746,1749,1752,1755,1758,1761,1764,1767,1770,1773,1775,1777,1780,1783,1786,1788,1791,1794,1797,1800,1803,1806,1809,1812,1815,1818,1821,1824,1827,1830,1833,1836,1839,1842,1845,1848,1851,1854,1857,1860],{"path":1623,"title":1624},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fchoosing-between-astro-and-eleventy-for-large-docs","Astro vs Eleventy for Large Docs (1000+ Pages)",{"path":1626,"title":1627},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites\u002Fcontent-collections-vs-eleventy-data-cascade","Content Collections vs the Eleventy Data Cascade",{"path":1629,"title":1630},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1632,"title":1633},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fhow-to-benchmark-hugo-vs-astro-build-speeds","How to Benchmark Hugo vs Astro Build Speeds",{"path":1635,"title":1636},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1638,"title":1639},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fprofiling-hugo-templates-with-template-metrics","Profiling Hugo Templates With Template Metrics",{"path":1641,"title":1642},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories\u002Fspeeding-up-hugo-builds-with-render-hooks-and-caching","Speeding Up Hugo Builds with Render Hooks & Caching",{"path":1644,"title":1645},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1647,"title":1648},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Feleventy-vs-jekyll-for-markdown-heavy-blogs","Eleventy vs Jekyll for Markdown-Heavy Blogs",{"path":1650,"title":1651},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1653,"title":1654},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Freplacing-jekyll-plugins-when-migrating-to-eleventy","Replacing Jekyll Plugins When Migrating to Eleventy",{"path":1656,"title":1657},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem\u002Frunning-jekyll-on-github-pages-without-plugins","Running Jekyll on GitHub Pages Without Plugins",{"path":1659,"title":1660},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1662,"title":1663},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fkeeping-redirects-working-after-an-ssg-migration","Keeping Redirects Working After an SSG Migration",{"path":1665,"title":1666},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-a-docs-site-from-jekyll-to-hugo","Migrating a Docs Site From Jekyll to Hugo",{"path":1668,"title":1669},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fmigrating-from-hugo-to-astro-without-breaking-urls","Migrating From Hugo to Astro Without Breaking URLs",{"path":1671,"title":1672},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators\u002Fporting-shortcodes-and-includes-between-generators","Porting Shortcodes and Includes Between Generators",{"path":1674,"title":1675},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export","Handling Dynamic Routes in Next.js Static Export",{"path":1677,"title":1678},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1680,"title":1681},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fmigrating-from-gatsby-to-nextjs-static-export","Migrating from Gatsby to Next.js Static Export",{"path":1683,"title":1684},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fnextjs-static-export-vs-astro-for-marketing-sites","Next.js Static Export vs Astro for Marketing",{"path":1686,"title":1687},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fbest-ssg-for-technical-writers-without-coding-experience","Best SSG for Non-Developer Technical Writers",{"path":1689,"title":1690},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fchoosing-an-ssg-for-api-reference-documentation","Choosing an SSG for API Reference Documentation",{"path":1692,"title":1693},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1695,"title":1696},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fpicking-an-ssg-for-a-multi-language-documentation-site","Picking an SSG for a Multi-Language Docs Site",{"path":1698,"title":1699},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fssg-selection-checklist-for-engineering-teams","SSG Selection Checklist for Engineering Teams",{"path":1701,"title":1702},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1704,"title":1705},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fpurging-the-cdn-cache-after-a-static-deploy","Purging the CDN Cache After a Static Deploy",{"path":1707,"title":1708},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-cache-control-headers-on-cloudflare-pages","Cache-Control Headers on Cloudflare Pages",{"path":1710,"title":1711},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs\u002Fsetting-up-proper-cache-headers-on-netlify","Proper Cache Headers on Netlify for SSGs",{"path":1713,"title":1714},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts","Eliminating Layout Shift From Web Fonts",{"path":1616,"title":5},{"path":1717,"title":24},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites",{"path":1719,"title":1480},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Fmeasuring-cls-in-the-field-with-web-vitals-js",{"path":1721,"title":1547},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift",{"path":1723,"title":1724},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1726,"title":1727},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fself-hosting-google-fonts-to-eliminate-layout-shift","Self-Host Google Fonts to Eliminate Layout Shift",{"path":1729,"title":1730},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites\u002Fsubsetting-variable-fonts-for-faster-first-render","Subsetting Variable Fonts for Faster First Render",{"path":1732,"title":1733},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fbuilding-an-image-cdn-pipeline-for-static-sites","Building an Image CDN Pipeline for Static Sites",{"path":1735,"title":1736},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1738,"title":1739},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins","Optimizing WebP Images in Hugo Without Plugins",{"path":1741,"title":1742},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Fserving-avif-with-fallbacks-on-static-sites","Serving AVIF With Fallbacks on Static Sites",{"path":1744,"title":1745},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1747,"title":1748},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1750,"title":1751},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fhow-to-reduce-bundle-size-in-eleventy-builds","How to Reduce Bundle Size in Eleventy Builds",{"path":1753,"title":1754},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1756,"title":1757},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fmeasuring-inp-on-static-sites-with-real-user-monitoring","Measuring INP on Static Sites with RUM",{"path":1759,"title":1760},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Feliminating-render-blocking-css-on-static-sites","Eliminating Render-Blocking CSS on Static Sites",{"path":1762,"title":1763},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1765,"title":1766},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Foptimizing-lcp-on-astro-with-priority-hints","Optimizing LCP on Astro with Priority Hints",{"path":1768,"title":1769},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Freducing-lcp-from-hero-images-on-static-sites","Reducing LCP from Hero Images on Static Sites",{"path":1771,"title":1772},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fauditing-third-party-scripts-with-lighthouse","Auditing Third-Party Scripts With Lighthouse",{"path":1774,"title":1559},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites",{"path":1776,"title":916},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Flazy-loading-youtube-embeds-on-static-sites",{"path":1778,"title":1779},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Fself-hosting-analytics-to-cut-third-party-requests","Self-Hosting Analytics to Cut Third-Party Requests",{"path":1781,"title":1782},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1784,"title":1785},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fdeploying-hugo-to-cloudflare-pages-and-workers","Deploying Hugo to Cloudflare Pages and Workers",{"path":1787,"title":933},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup",{"path":1789,"title":1790},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fdocs-as-code-review-workflow-for-writers","Docs-as-Code Review Workflow for Writers",{"path":1792,"title":1793},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":1795,"title":1796},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds","Scheduling Content Publication With Cron-Triggered Builds",{"path":1798,"title":1799},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fwiring-a-headless-cms-to-a-static-build","Wiring a Headless CMS to a Static Build",{"path":1801,"title":1802},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fcaching-node-modules-in-github-actions-for-faster-ssg-builds","Caching node_modules in GitHub Actions",{"path":1804,"title":1805},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fdeploying-to-multiple-environments-from-one-workflow","Deploying to Multiple Environments From One Workflow",{"path":1807,"title":1808},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002Fhow-to-set-up-github-actions-for-hugo-deployments","GitHub Actions for Hugo Deployments",{"path":1810,"title":1811},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":1813,"title":1814},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fcaching-hugo-builds-in-github-actions","Caching Hugo Builds in GitHub Actions",{"path":1816,"title":1817},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1819,"title":1820},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":1822,"title":1823},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fmeasuring-build-time-regressions-in-ci","Measuring Build-Time Regressions in CI",{"path":1825,"title":1826},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fsharing-build-cache-across-ci-runners","Sharing Build Cache Across CI Runners",{"path":1828,"title":1829},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1831,"title":1832},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":1834,"title":1835},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":1837,"title":1838},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fsetting-up-deploy-previews-on-netlify-for-every-pull-request","Netlify Deploy Previews for Every Pull Request",{"path":1840,"title":1841},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fvercel-isr-vs-static-generation-for-ssgs","Vercel ISR vs Static Generation for SSGs",{"path":1843,"title":1844},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fautomating-preview-deploy-pipelines-with-github-actions","Automating Preview Deploy Pipelines with GitHub Actions",{"path":1846,"title":1847},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":1849,"title":1850},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":1852,"title":1853},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":1855,"title":1856},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":1858,"title":1859},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frolling-back-a-bad-static-deploy-in-under-a-minute","Rolling Back a Bad Static Deploy in Under a Minute",{"path":1861,"title":1862},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Frunning-smoke-tests-against-a-preview-url","Running Smoke Tests Against a Preview URL",1785611671075]