[{"data":1,"prerenderedAt":1908},["ShallowReactive",2],{"page:\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift":3,"all-docs-nav":1666},{"id":4,"title":5,"body":6,"breadcrumb":1638,"dateModified":1647,"datePublished":1647,"description":1648,"extension":1649,"faq":1650,"meta":1659,"navigation":925,"path":1660,"seo":1661,"slug":1662,"stem":1663,"type":1664,"__hash__":1665},"content\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift\u002Findex.md","Reserving Space for Images and Embeds",{"type":7,"value":8,"toc":1616},"minimark",[9,13,17,35,40,66,70,85,210,214,219,222,300,344,356,360,371,448,464,468,481,605,609,612,718,725,728,865,871,875,878,1246,1253,1257,1260,1347,1437,1444,1448,1508,1512,1525,1529,1533,1539,1543,1546,1550,1556,1560,1566,1570,1573,1577,1612],[10,11,5],"h1",{"id":12},"reserving-space-for-images-and-embeds",[14,15,16],"p",{},"An image with no declared dimensions occupies zero height until its bytes arrive, so every element below it renders in the wrong place and then jumps. This is the single largest source of Cumulative Layout Shift on content sites, and on a static site it is entirely preventable: the generator has the file on disk at build time and knows exactly how big it is.",[14,18,19,20,24,25,28,29,34],{},"This guide is the concrete recipe — what to emit for plain images, responsive ",[21,22,23],"code",{},"srcset",", art-directed ",[21,26,27],{},"\u003Cpicture>",", and embeds that have no intrinsic size at all — plus the numbers from applying it to a documentation template. It is the media-specific piece of ",[30,31,33],"a",{"href":32},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002F","Cumulative Layout Shift Fixes for Static Sites",".",[36,37,39],"h2",{"id":38},"prerequisites","Prerequisites",[41,42,43,55,63],"ul",{},[44,45,46,47,50,51,54],"li",{},"A static site where images are processed at build time (Astro ",[21,48,49],{},"astro:assets",", Hugo image resources, ",[21,52,53],{},"@11ty\u002Feleventy-img",", or a pre-processing step of your own).",[44,56,57,58,62],{},"Chrome DevTools for the Performance panel's ",[59,60,61],"em",{},"Layout Shifts"," track, or the Layout Instability script from the parent guide.",[44,64,65],{},"A baseline CLS number per template so you can prove the change worked.",[36,67,69],{"id":68},"why-the-attributes-still-matter","Why the Attributes Still Matter",[14,71,72,73,76,77,80,81,84],{},"Browsers compute an intrinsic aspect ratio from the ",[21,74,75],{},"width"," and ",[21,78,79],{},"height"," attributes before any CSS is applied. That gives the layout engine a box to reserve during the very first pass — before the image request has even resolved. CSS ",[21,82,83],{},"aspect-ratio"," cannot do this alone, because a stylesheet arrives after the HTML has begun laying out, and because the property needs at least one known dimension to work from.",[86,87,88,206],"figure",{},[89,90,97,98,97,102,97,106,97,113],"svg",{"viewBox":91,"role":92,"ariaLabelledBy":93,"xmlns":96},"0 0 760 300","img",[94,95],"rsv-box-title","rsv-box-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[99,100,101],"title",{"id":94},"Reserved box versus collapsed box during image load",[103,104,105],"desc",{"id":95},"Two page columns compared over time. Without dimensions the image area is zero height at 0.2 seconds and the following paragraph sits high, then jumps 320 pixels when the image decodes at 1.4 seconds. With width and height attributes the box is reserved at 0.2 seconds and the paragraph never moves.",[107,108],"rect",{"x":109,"y":109,"width":110,"height":111,"fill":112},"0","760","300","#ffffff",[114,115,117,118,117,126,117,133,117,143,117,148,117,154,117,158,117,163,117,170,117,174,117,179,117,184,117,187,117,191,117,193,117,197,117,201,97],"g",{"style":116},"font-family:system-ui, sans-serif;font-size:13px","\n    ",[119,120,125],"text",{"x":121,"y":122,"fill":123,"style":124},"380","26","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","Same image, two first layout passes",[119,127,132],{"x":128,"y":129,"fill":130,"style":131},"200","52","#d83b41","font-size:13px;font-weight:700;text-anchor:middle","No width or height",[107,134],{"x":135,"y":136,"width":137,"height":122,"rx":138,"fill":139,"opacity":140,"stroke":141,"style":142},"60","64","280","5","#d9e2ef","0.6","#556071","stroke-width:1.5px",[119,144,147],{"x":128,"y":145,"fill":123,"style":146},"82","font-size:12px;text-anchor:middle","image box = 0 px tall",[107,149],{"x":135,"y":150,"width":137,"height":129,"rx":151,"fill":152,"opacity":153,"stroke":130,"style":142},"100","6","#ff595e","0.14",[119,155,157],{"x":128,"y":156,"fill":123,"style":146},"122","paragraph renders here at 0.2 s",[119,159,162],{"x":128,"y":160,"fill":123,"style":161},"140","font-size:12px;font-weight:700;text-anchor:middle","then drops 320 px at 1.4 s",[107,164],{"x":135,"y":165,"width":137,"height":166,"rx":151,"fill":167,"opacity":168,"stroke":169,"style":142},"166","86","#ffca3a","0.28","#a97b00",[119,171,173],{"x":128,"y":172,"fill":123,"style":146},"214","image finally occupies its space",[119,175,178],{"x":176,"y":129,"fill":177,"style":131},"560","#5a8a16","width=1600 height=900",[107,180],{"x":181,"y":136,"width":137,"height":166,"rx":151,"fill":182,"opacity":183,"stroke":177,"style":142},"420","#8ac926","0.18",[119,185,186],{"x":176,"y":150,"fill":123,"style":146},"box reserved at 0.2 s",[119,188,190],{"x":176,"y":189,"fill":141,"style":146},"120","empty, correct height",[107,192],{"x":181,"y":165,"width":137,"height":129,"rx":151,"fill":182,"opacity":183,"stroke":177,"style":142},[119,194,196],{"x":176,"y":195,"fill":123,"style":146},"188","paragraph renders in final position",[119,198,200],{"x":176,"y":199,"fill":123,"style":161},"206","0 px movement · CLS 0.00",[119,202,205],{"x":135,"y":203,"fill":141,"style":204},"278","font-size:12px","Only the ratio of the attributes is used — the CSS width still controls the rendered size",[207,208,209],"figcaption",{},"The attributes are a promise about shape, not size: the browser reserves a correctly proportioned box immediately and scales it to whatever the CSS says.",[36,211,213],{"id":212},"the-recipe","The Recipe",[215,216,218],"h3",{"id":217},"_1-plain-images","1. Plain images",[14,220,221],{},"Emit the intrinsic dimensions of the source file and let CSS control the rendered width:",[223,224,229],"pre",{"className":225,"code":226,"language":227,"meta":228,"style":228},"language-html shiki shiki-themes github-light github-dark","\u003Cimg src=\"\u002Fimg\u002Fpipeline.avif\" width=\"1600\" height=\"900\"\n     alt=\"Build pipeline stages\" loading=\"lazy\" decoding=\"async\">\n","html","",[21,230,231,270],{"__ignoreMap":228},[232,233,236,240,243,247,250,254,257,259,262,265,267],"span",{"class":234,"line":235},"line",1,[232,237,239],{"class":238},"sVt8B","\u003C",[232,241,92],{"class":242},"s9eBZ",[232,244,246],{"class":245},"sScJk"," src",[232,248,249],{"class":238},"=",[232,251,253],{"class":252},"sZZnC","\"\u002Fimg\u002Fpipeline.avif\"",[232,255,256],{"class":245}," width",[232,258,249],{"class":238},[232,260,261],{"class":252},"\"1600\"",[232,263,264],{"class":245}," height",[232,266,249],{"class":238},[232,268,269],{"class":252},"\"900\"\n",[232,271,273,276,278,281,284,286,289,292,294,297],{"class":234,"line":272},2,[232,274,275],{"class":245},"     alt",[232,277,249],{"class":238},[232,279,280],{"class":252},"\"Build pipeline stages\"",[232,282,283],{"class":245}," loading",[232,285,249],{"class":238},[232,287,288],{"class":252},"\"lazy\"",[232,290,291],{"class":245}," decoding",[232,293,249],{"class":238},[232,295,296],{"class":252},"\"async\"",[232,298,299],{"class":238},">\n",[223,301,305],{"className":302,"code":303,"language":304,"meta":228,"style":228},"language-css shiki shiki-themes github-light github-dark",".prose img { max-width: 100%; height: auto; }\n","css",[21,306,307],{"__ignoreMap":228},[232,308,309,312,315,318,322,325,327,331,334,336,338,341],{"class":234,"line":235},[232,310,311],{"class":245},".prose",[232,313,314],{"class":242}," img",[232,316,317],{"class":238}," { ",[232,319,321],{"class":320},"sj4cs","max-width",[232,323,324],{"class":238},": ",[232,326,150],{"class":320},[232,328,330],{"class":329},"szBVR","%",[232,332,333],{"class":238},"; ",[232,335,79],{"class":320},[232,337,324],{"class":238},[232,339,340],{"class":320},"auto",[232,342,343],{"class":238},"; }\n",[14,345,346,349,350,352,353,355],{},[21,347,348],{},"height: auto"," is what keeps the ratio honest once ",[21,351,321],{}," shrinks the image; without it the browser uses the literal ",[21,354,79],{}," attribute and distorts the picture on narrow screens.",[215,357,359],{"id":358},"_2-responsive-srcset","2. Responsive srcset",[14,361,362,364,365,367,368,370],{},[21,363,23],{}," candidates all share one intrinsic ratio, so a single ",[21,366,75],{},"\u002F",[21,369,79],{}," pair still describes the box:",[223,372,374],{"className":225,"code":373,"language":227,"meta":228,"style":228},"\u003Cimg\n  srcset=\"\u002Fimg\u002Fhero-480.avif 480w, \u002Fimg\u002Fhero-960.avif 960w, \u002Fimg\u002Fhero-1600.avif 1600w\"\n  sizes=\"(min-width: 768px) 720px, 100vw\"\n  src=\"\u002Fimg\u002Fhero-960.avif\" width=\"1600\" height=\"900\"\n  alt=\"Deployment dashboard\" fetchpriority=\"high\">\n",[21,375,376,383,393,404,427],{"__ignoreMap":228},[232,377,378,380],{"class":234,"line":235},[232,379,239],{"class":238},[232,381,382],{"class":242},"img\n",[232,384,385,388,390],{"class":234,"line":272},[232,386,387],{"class":245},"  srcset",[232,389,249],{"class":238},[232,391,392],{"class":252},"\"\u002Fimg\u002Fhero-480.avif 480w, \u002Fimg\u002Fhero-960.avif 960w, \u002Fimg\u002Fhero-1600.avif 1600w\"\n",[232,394,396,399,401],{"class":234,"line":395},3,[232,397,398],{"class":245},"  sizes",[232,400,249],{"class":238},[232,402,403],{"class":252},"\"(min-width: 768px) 720px, 100vw\"\n",[232,405,407,410,412,415,417,419,421,423,425],{"class":234,"line":406},4,[232,408,409],{"class":245},"  src",[232,411,249],{"class":238},[232,413,414],{"class":252},"\"\u002Fimg\u002Fhero-960.avif\"",[232,416,256],{"class":245},[232,418,249],{"class":238},[232,420,261],{"class":252},[232,422,264],{"class":245},[232,424,249],{"class":238},[232,426,269],{"class":252},[232,428,430,433,435,438,441,443,446],{"class":234,"line":429},5,[232,431,432],{"class":245},"  alt",[232,434,249],{"class":238},[232,436,437],{"class":252},"\"Deployment dashboard\"",[232,439,440],{"class":245}," fetchpriority",[232,442,249],{"class":238},[232,444,445],{"class":252},"\"high\"",[232,447,299],{"class":238},[14,449,450,451,454,455,459,460,34],{},"Generate the candidates from the same source so the ratio cannot drift. Astro's ",[21,452,453],{},"\u003CPicture>"," does this automatically, as covered in ",[30,456,458],{"href":457},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002F","Image Optimization Pipelines in Astro","; the equivalent Hugo approach is in ",[30,461,463],{"href":462},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins\u002F","Optimizing WebP Images in Hugo Without Plugins",[215,465,467],{"id":466},"_3-art-directed-picture-elements","3. Art-directed picture elements",[14,469,470,471,474,475,477,478,480],{},"This is where reservations break. If a mobile ",[21,472,473],{},"\u003Csource>"," is a 1:1 crop and the desktop ",[21,476,473],{}," is 16:9, the box has to change at the breakpoint. Put the dimensions on each ",[21,479,473],{}," so the browser knows the ratio for the media query it matched:",[223,482,484],{"className":225,"code":483,"language":227,"meta":228,"style":228},"\u003Cpicture>\n  \u003Csource media=\"(min-width: 768px)\" srcset=\"\u002Fimg\u002Fwide.avif\" width=\"1600\" height=\"900\">\n  \u003Csource srcset=\"\u002Fimg\u002Fsquare.avif\" width=\"800\" height=\"800\">\n  \u003Cimg src=\"\u002Fimg\u002Fwide.avif\" width=\"1600\" height=\"900\" alt=\"Release timeline\">\n\u003C\u002Fpicture>\n",[21,485,486,495,534,562,596],{"__ignoreMap":228},[232,487,488,490,493],{"class":234,"line":235},[232,489,239],{"class":238},[232,491,492],{"class":242},"picture",[232,494,299],{"class":238},[232,496,497,500,503,506,508,511,514,516,519,521,523,525,527,529,532],{"class":234,"line":272},[232,498,499],{"class":238},"  \u003C",[232,501,502],{"class":242},"source",[232,504,505],{"class":245}," media",[232,507,249],{"class":238},[232,509,510],{"class":252},"\"(min-width: 768px)\"",[232,512,513],{"class":245}," srcset",[232,515,249],{"class":238},[232,517,518],{"class":252},"\"\u002Fimg\u002Fwide.avif\"",[232,520,256],{"class":245},[232,522,249],{"class":238},[232,524,261],{"class":252},[232,526,264],{"class":245},[232,528,249],{"class":238},[232,530,531],{"class":252},"\"900\"",[232,533,299],{"class":238},[232,535,536,538,540,542,544,547,549,551,554,556,558,560],{"class":234,"line":395},[232,537,499],{"class":238},[232,539,502],{"class":242},[232,541,513],{"class":245},[232,543,249],{"class":238},[232,545,546],{"class":252},"\"\u002Fimg\u002Fsquare.avif\"",[232,548,256],{"class":245},[232,550,249],{"class":238},[232,552,553],{"class":252},"\"800\"",[232,555,264],{"class":245},[232,557,249],{"class":238},[232,559,553],{"class":252},[232,561,299],{"class":238},[232,563,564,566,568,570,572,574,576,578,580,582,584,586,589,591,594],{"class":234,"line":406},[232,565,499],{"class":238},[232,567,92],{"class":242},[232,569,246],{"class":245},[232,571,249],{"class":238},[232,573,518],{"class":252},[232,575,256],{"class":245},[232,577,249],{"class":238},[232,579,261],{"class":252},[232,581,264],{"class":245},[232,583,249],{"class":238},[232,585,531],{"class":252},[232,587,588],{"class":245}," alt",[232,590,249],{"class":238},[232,592,593],{"class":252},"\"Release timeline\"",[232,595,299],{"class":238},[232,597,598,601,603],{"class":234,"line":429},[232,599,600],{"class":238},"\u003C\u002F",[232,602,492],{"class":242},[232,604,299],{"class":238},[215,606,608],{"id":607},"_4-iframes-and-embeds","4. Iframes and embeds",[14,610,611],{},"An iframe has no intrinsic size, so the wrapper owns the reservation:",[223,613,615],{"className":302,"code":614,"language":304,"meta":228,"style":228},".embed { position: relative; width: 100%; aspect-ratio: 16 \u002F 9; }\n.embed > iframe { position: absolute; inset: 0; width: 100%; height: 100%; border: 0; }\n",[21,616,617,659],{"__ignoreMap":228},[232,618,619,622,624,627,629,632,634,636,638,640,642,644,646,648,651,654,657],{"class":234,"line":235},[232,620,621],{"class":245},".embed",[232,623,317],{"class":238},[232,625,626],{"class":320},"position",[232,628,324],{"class":238},[232,630,631],{"class":320},"relative",[232,633,333],{"class":238},[232,635,75],{"class":320},[232,637,324],{"class":238},[232,639,150],{"class":320},[232,641,330],{"class":329},[232,643,333],{"class":238},[232,645,83],{"class":320},[232,647,324],{"class":238},[232,649,650],{"class":320},"16",[232,652,653],{"class":238}," \u002F ",[232,655,656],{"class":320},"9",[232,658,343],{"class":238},[232,660,661,663,666,669,671,673,675,678,680,683,685,687,689,691,693,695,697,699,701,703,705,707,709,712,714,716],{"class":234,"line":272},[232,662,621],{"class":245},[232,664,665],{"class":329}," >",[232,667,668],{"class":242}," iframe",[232,670,317],{"class":238},[232,672,626],{"class":320},[232,674,324],{"class":238},[232,676,677],{"class":320},"absolute",[232,679,333],{"class":238},[232,681,682],{"class":320},"inset",[232,684,324],{"class":238},[232,686,109],{"class":320},[232,688,333],{"class":238},[232,690,75],{"class":320},[232,692,324],{"class":238},[232,694,150],{"class":320},[232,696,330],{"class":329},[232,698,333],{"class":238},[232,700,79],{"class":320},[232,702,324],{"class":238},[232,704,150],{"class":320},[232,706,330],{"class":329},[232,708,333],{"class":238},[232,710,711],{"class":320},"border",[232,713,324],{"class":238},[232,715,109],{"class":320},[232,717,343],{"class":238},[14,719,720,721,724],{},"For embeds without a fixed ratio — a comment thread, a newsletter form, a status widget — reserve a realistic ",[21,722,723],{},"min-height"," instead and let the content grow downward from a stable top edge. A wrong-by-40-pixels reservation costs a fraction of the score that a zero-height reservation does.",[14,726,727],{},"Which technique applies is decided entirely by what you know at build time, and the decision is small enough to memorise:",[86,729,730,862],{},[89,731,97,736,97,739,97,742,97,745,97,848],{"viewBox":732,"role":92,"ariaLabelledBy":733,"xmlns":96},"0 0 760 320",[734,735],"rsv-tree-title","rsv-tree-desc",[99,737,738],{"id":734},"Choosing a space-reservation technique",[103,740,741],{"id":735},"A decision tree. If the intrinsic size is known at build time, emit width and height attributes. If not, ask whether the ratio is fixed: a fixed ratio gets an aspect-ratio wrapper, a variable one gets a min-height reservation on the container.",[107,743],{"x":109,"y":109,"width":110,"height":744,"fill":112},"320",[114,746,117,747,117,751,117,759,117,763,117,767,117,771,117,776,117,780,117,784,117,788,117,791,117,796,117,800,117,804,117,808,117,811,117,814,117,832,117,838,117,842,117,845,97],{"style":116},[119,748,750],{"x":121,"y":749,"fill":123,"style":124},"28","Pick the reservation by what the build knows",[107,752],{"x":753,"y":754,"width":755,"height":129,"rx":756,"fill":757,"opacity":153,"stroke":757,"style":758},"270","50","220","10","#6a4c93","stroke-width:2px",[119,760,762],{"x":121,"y":761,"fill":123,"style":131},"72","Intrinsic size known",[119,764,766],{"x":121,"y":765,"fill":123,"style":131},"90","at build time?",[107,768],{"x":135,"y":769,"width":770,"height":135,"rx":756,"fill":182,"opacity":183,"stroke":177,"style":758},"150","230",[119,772,775],{"x":773,"y":774,"fill":123,"style":131},"175","174","width + height attributes",[119,777,779],{"x":773,"y":778,"fill":141,"style":146},"194","images, picture sources",[107,781],{"x":782,"y":769,"width":770,"height":135,"rx":756,"fill":783,"opacity":153,"stroke":783,"style":758},"470","#1982c4",[119,785,787],{"x":786,"y":774,"fill":123,"style":131},"585","Is the ratio fixed?",[119,789,790],{"x":786,"y":778,"fill":141,"style":146},"video 16 \u002F 9, map 4 \u002F 3 …",[107,792],{"x":793,"y":794,"width":769,"height":795,"rx":756,"fill":182,"opacity":183,"stroke":177,"style":758},"400","242","56",[119,797,83],{"x":798,"y":799,"fill":123,"style":161},"475","266",[119,801,803],{"x":798,"y":802,"fill":141,"style":146},"284","wrapper",[107,805],{"x":806,"y":794,"width":769,"height":795,"rx":756,"fill":167,"opacity":807,"stroke":169,"style":758},"580","0.3",[119,809,723],{"x":810,"y":799,"fill":123,"style":161},"655",[119,812,813],{"x":810,"y":802,"fill":141,"style":146},"best estimate",[114,815,817,818,817,823,817,826,817,829,117],{"stroke":141,"fill":816,"style":758},"none","\n      ",[819,820],"path",{"d":821,"style":822},"M300 102 L200 148","marker-end:url(#rsv-tree-arrow)",[819,824],{"d":825,"style":822},"M460 102 L560 148",[819,827],{"d":828,"style":822},"M540 210 L490 240",[819,830],{"d":831,"style":822},"M620 210 L648 240",[119,833,837],{"x":834,"y":835,"fill":177,"style":836},"196","128","font-size:12px;font-weight:700","yes",[119,839,841],{"x":840,"y":835,"fill":130,"style":836},"520","no",[119,843,837],{"x":844,"y":770,"fill":177,"style":836},"452",[119,846,841],{"x":847,"y":770,"fill":169,"style":836},"640",[849,850,117,851,97],"defs",{},[852,853,817,859,117],"marker",{"id":854,"viewBox":855,"refX":856,"refY":138,"markerWidth":857,"markerHeight":857,"orient":858},"rsv-tree-arrow","0 0 10 10","8","7","auto-start-reverse",[819,860],{"d":861,"fill":141},"M0 0 L10 5 L0 10 z",[207,863,864],{},"Only the leftmost branch is free of guesswork, which is why moving media into the build pipeline pays off twice: smaller files and a reservation you never have to estimate.",[14,866,867,868,870],{},"The ",[21,869,723],{}," branch feels unsatisfying, but it is worth being precise about how much it buys. A widget that finally renders at 460 px in a container reserved for 420 px moves the content below it by 40 px rather than 460 px — an eleven-fold reduction in the distance fraction, and usually the difference between a failing and a passing score. Estimate from the median observed height in production rather than the smallest possible one, and re-check it when the widget's content changes.",[215,872,874],{"id":873},"_5-enforce-it-in-the-build","5. Enforce it in the build",[14,876,877],{},"Manual discipline decays. Fail the build when an image slips through:",[223,879,883],{"className":880,"code":881,"language":882,"meta":228,"style":228},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F scripts\u002Fcheck-img-dims.mjs — run after the build, over the generated HTML\nimport { readFileSync } from 'node:fs';\nimport { globSync } from 'node:fs';\n\nlet bad = 0;\nfor (const file of globSync('dist\u002F**\u002F*.html')) {\n  const html = readFileSync(file, 'utf8');\n  for (const [tag] of html.matchAll(\u002F\u003Cimg\\b[^>]*>\u002Fg)) {\n    if (!\u002F\\bwidth=\u002F.test(tag) || !\u002F\\bheight=\u002F.test(tag)) {\n      console.error(`${file}: ${tag.slice(0, 90)}`);\n      bad++;\n    }\n  }\n}\nif (bad) {\n  console.error(`check-img-dims: ${bad} image(s) without dimensions`);\n  process.exit(1);\n}\nconsole.log('check-img-dims: all images carry dimensions');\n","javascript",[21,884,885,891,908,921,927,942,972,996,1056,1106,1150,1161,1167,1173,1179,1188,1209,1225,1230],{"__ignoreMap":228},[232,886,887],{"class":234,"line":235},[232,888,890],{"class":889},"sJ8bj","\u002F\u002F scripts\u002Fcheck-img-dims.mjs — run after the build, over the generated HTML\n",[232,892,893,896,899,902,905],{"class":234,"line":272},[232,894,895],{"class":329},"import",[232,897,898],{"class":238}," { readFileSync } ",[232,900,901],{"class":329},"from",[232,903,904],{"class":252}," 'node:fs'",[232,906,907],{"class":238},";\n",[232,909,910,912,915,917,919],{"class":234,"line":395},[232,911,895],{"class":329},[232,913,914],{"class":238}," { globSync } ",[232,916,901],{"class":329},[232,918,904],{"class":252},[232,920,907],{"class":238},[232,922,923],{"class":234,"line":406},[232,924,926],{"emptyLinePlaceholder":925},true,"\n",[232,928,929,932,935,937,940],{"class":234,"line":429},[232,930,931],{"class":329},"let",[232,933,934],{"class":238}," bad ",[232,936,249],{"class":329},[232,938,939],{"class":320}," 0",[232,941,907],{"class":238},[232,943,945,948,951,954,957,960,963,966,969],{"class":234,"line":944},6,[232,946,947],{"class":329},"for",[232,949,950],{"class":238}," (",[232,952,953],{"class":329},"const",[232,955,956],{"class":320}," file",[232,958,959],{"class":329}," of",[232,961,962],{"class":245}," globSync",[232,964,965],{"class":238},"(",[232,967,968],{"class":252},"'dist\u002F**\u002F*.html'",[232,970,971],{"class":238},")) {\n",[232,973,975,978,981,984,987,990,993],{"class":234,"line":974},7,[232,976,977],{"class":329},"  const",[232,979,980],{"class":320}," html",[232,982,983],{"class":329}," =",[232,985,986],{"class":245}," readFileSync",[232,988,989],{"class":238},"(file, ",[232,991,992],{"class":252},"'utf8'",[232,994,995],{"class":238},");\n",[232,997,999,1002,1004,1006,1009,1012,1015,1018,1021,1024,1026,1028,1032,1035,1038,1041,1044,1047,1050,1052,1054],{"class":234,"line":998},8,[232,1000,1001],{"class":329},"  for",[232,1003,950],{"class":238},[232,1005,953],{"class":329},[232,1007,1008],{"class":238}," [",[232,1010,1011],{"class":320},"tag",[232,1013,1014],{"class":238},"] ",[232,1016,1017],{"class":329},"of",[232,1019,1020],{"class":238}," html.",[232,1022,1023],{"class":245},"matchAll",[232,1025,965],{"class":238},[232,1027,367],{"class":252},[232,1029,1031],{"class":1030},"sA_wV","\u003Cimg",[232,1033,1034],{"class":329},"\\b",[232,1036,1037],{"class":320},"[",[232,1039,1040],{"class":329},"^",[232,1042,1043],{"class":320},">]",[232,1045,1046],{"class":329},"*",[232,1048,1049],{"class":1030},">",[232,1051,367],{"class":252},[232,1053,114],{"class":329},[232,1055,971],{"class":238},[232,1057,1059,1062,1064,1067,1069,1071,1074,1076,1078,1081,1084,1087,1090,1092,1094,1097,1099,1101,1103],{"class":234,"line":1058},9,[232,1060,1061],{"class":329},"    if",[232,1063,950],{"class":238},[232,1065,1066],{"class":329},"!",[232,1068,367],{"class":252},[232,1070,1034],{"class":329},[232,1072,1073],{"class":1030},"width=",[232,1075,367],{"class":252},[232,1077,34],{"class":238},[232,1079,1080],{"class":245},"test",[232,1082,1083],{"class":238},"(tag) ",[232,1085,1086],{"class":329},"||",[232,1088,1089],{"class":329}," !",[232,1091,367],{"class":252},[232,1093,1034],{"class":329},[232,1095,1096],{"class":1030},"height=",[232,1098,367],{"class":252},[232,1100,34],{"class":238},[232,1102,1080],{"class":245},[232,1104,1105],{"class":238},"(tag)) {\n",[232,1107,1109,1112,1115,1117,1120,1123,1126,1128,1130,1133,1135,1137,1140,1142,1145,1148],{"class":234,"line":1108},10,[232,1110,1111],{"class":238},"      console.",[232,1113,1114],{"class":245},"error",[232,1116,965],{"class":238},[232,1118,1119],{"class":252},"`${",[232,1121,1122],{"class":238},"file",[232,1124,1125],{"class":252},"}: ${",[232,1127,1011],{"class":238},[232,1129,34],{"class":252},[232,1131,1132],{"class":245},"slice",[232,1134,965],{"class":252},[232,1136,109],{"class":320},[232,1138,1139],{"class":252},", ",[232,1141,765],{"class":320},[232,1143,1144],{"class":252},")",[232,1146,1147],{"class":252},"}`",[232,1149,995],{"class":238},[232,1151,1153,1156,1159],{"class":234,"line":1152},11,[232,1154,1155],{"class":238},"      bad",[232,1157,1158],{"class":329},"++",[232,1160,907],{"class":238},[232,1162,1164],{"class":234,"line":1163},12,[232,1165,1166],{"class":238},"    }\n",[232,1168,1170],{"class":234,"line":1169},13,[232,1171,1172],{"class":238},"  }\n",[232,1174,1176],{"class":234,"line":1175},14,[232,1177,1178],{"class":238},"}\n",[232,1180,1182,1185],{"class":234,"line":1181},15,[232,1183,1184],{"class":329},"if",[232,1186,1187],{"class":238}," (bad) {\n",[232,1189,1191,1194,1196,1198,1201,1204,1207],{"class":234,"line":1190},16,[232,1192,1193],{"class":238},"  console.",[232,1195,1114],{"class":245},[232,1197,965],{"class":238},[232,1199,1200],{"class":252},"`check-img-dims: ${",[232,1202,1203],{"class":238},"bad",[232,1205,1206],{"class":252},"} image(s) without dimensions`",[232,1208,995],{"class":238},[232,1210,1212,1215,1218,1220,1223],{"class":234,"line":1211},17,[232,1213,1214],{"class":238},"  process.",[232,1216,1217],{"class":245},"exit",[232,1219,965],{"class":238},[232,1221,1222],{"class":320},"1",[232,1224,995],{"class":238},[232,1226,1228],{"class":234,"line":1227},18,[232,1229,1178],{"class":238},[232,1231,1233,1236,1239,1241,1244],{"class":234,"line":1232},19,[232,1234,1235],{"class":238},"console.",[232,1237,1238],{"class":245},"log",[232,1240,965],{"class":238},[232,1242,1243],{"class":252},"'check-img-dims: all images carry dimensions'",[232,1245,995],{"class":238},[14,1247,1248,1249,34],{},"Wire it into the same CI job that runs your other content gates — the pattern is the same one used for build-time checks in ",[30,1250,1252],{"href":1251},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds\u002F","GitHub Actions for Automated SSG Builds",[36,1254,1256],{"id":1255},"measured-impact","Measured Impact",[14,1258,1259],{},"Measured on a documentation template with six body images and one video embed, Chrome 4× CPU throttle on a fast 3G profile, median of five runs:",[1261,1262,1263,1282],"table",{},[1264,1265,1266],"thead",{},[1267,1268,1269,1273,1276,1279],"tr",{},[1270,1271,1272],"th",{},"Change",[1270,1274,1275],{},"CLS",[1270,1277,1278],{},"LCP",[1270,1280,1281],{},"Notes",[1283,1284,1285,1300,1317,1330],"tbody",{},[1267,1286,1287,1291,1294,1297],{},[1288,1289,1290],"td",{},"Baseline (no dimensions)",[1288,1292,1293],{},"0.31",[1288,1295,1296],{},"2.9 s",[1288,1298,1299],{},"Every body image collapses to zero height",[1267,1301,1302,1309,1312,1314],{},[1288,1303,1304,367,1306,1308],{},[21,1305,75],{},[21,1307,79],{}," on body images",[1288,1310,1311],{},"0.07",[1288,1313,1296],{},[1288,1315,1316],{},"Remaining shift is the video embed",[1267,1318,1319,1322,1325,1327],{},[1288,1320,1321],{},"Aspect-ratio wrapper on the embed",[1288,1323,1324],{},"0.01",[1288,1326,1296],{},[1288,1328,1329],{},"Residual is a late-swapping web font",[1267,1331,1332,1339,1341,1344],{},[1288,1333,1334,1335,1338],{},"Above + ",[21,1336,1337],{},"loading=\"lazy\""," below fold",[1288,1340,1324],{},[1288,1342,1343],{},"2.4 s",[1288,1345,1346],{},"380 KB fewer bytes on first load",[86,1348,1349,1434],{},[89,1350,97,1355,97,1358,97,1361,97,1363],{"viewBox":1351,"role":92,"ariaLabelledBy":1352,"xmlns":96},"0 0 760 280",[1353,1354],"rsv-bars-title","rsv-bars-desc",[99,1356,1357],{"id":1353},"CLS by fix on a documentation template",[103,1359,1360],{"id":1354},"A horizontal bar chart on a shared scale. Baseline scores 0.31, adding image dimensions drops it to 0.07, adding the embed aspect-ratio wrapper drops it to 0.01, and adding lazy loading keeps it at 0.01 while removing 380 kilobytes. The 0.10 good threshold is marked with a dashed line.",[107,1362],{"x":109,"y":109,"width":110,"height":137,"fill":112},[114,1364,117,1366,117,1369,117,1374,117,1378,117,1384,117,1387,117,1392,117,1398,117,1402,117,1405,117,1409,117,1413,117,1416,117,1420,117,1423,117,1428,117,1430,97],{"style":1365},"font-family:system-ui, sans-serif;font-size:12px",[119,1367,1368],{"x":121,"y":122,"fill":123,"style":124},"Where the score actually goes",[234,1370],{"x1":1371,"y1":1372,"x2":1371,"y2":1373,"stroke":139,"style":142},"250","44","232",[234,1375],{"x1":1376,"y1":1372,"x2":1376,"y2":1373,"stroke":182,"style":1377},"394","stroke-width:2px;stroke-dasharray:6 5",[119,1379,1383],{"x":1380,"y":1381,"fill":177,"style":1382},"404","58","font-size:11px;font-weight:700","0.10 threshold",[119,1385,1386],{"x":135,"y":145,"fill":123,"style":836},"Baseline",[119,1388,1391],{"x":135,"y":1389,"fill":141,"style":1390},"98","font-size:11px","no dimensions",[107,1393],{"x":1371,"y":1394,"width":1395,"height":1396,"rx":138,"fill":152,"opacity":1397,"stroke":130,"style":142},"70","446","30","0.16",[119,1399,1401],{"x":1400,"y":765,"fill":130,"style":161},"600","CLS 0.31",[119,1403,1404],{"x":135,"y":160,"fill":123,"style":836},"+ image dims",[119,1406,1408],{"x":135,"y":1407,"fill":141,"style":1390},"156","6 body images",[107,1410],{"x":1371,"y":835,"width":1411,"height":1396,"rx":138,"fill":167,"opacity":1412,"stroke":169,"style":142},"101","0.42",[119,1414,1311],{"x":111,"y":1415,"fill":123,"style":161},"148",[119,1417,1419],{"x":135,"y":1418,"fill":123,"style":836},"198","+ embed box",[119,1421,1422],{"x":135,"y":172,"fill":141,"style":1390},"16 \u002F 9 wrapper",[107,1424],{"x":1371,"y":1425,"width":1426,"height":1396,"rx":1427,"fill":182,"opacity":807,"stroke":177,"style":142},"186","15","4",[119,1429,1324],{"x":203,"y":199,"fill":177,"style":836},[119,1431,1433],{"x":1371,"y":1432,"fill":141,"style":1390},"258","Shared linear scale · Chrome 4× CPU throttle, fast 3G, median of 5 runs",[207,1435,1436],{},"Dimensions on the body images removed 77% of the score; the embed wrapper removed most of what was left. Neither change touched a single byte of image data.",[14,1438,1439,1440,1443],{},"The LCP row is worth noting: reserving space did not make the page faster, it made it ",[59,1441,1442],{},"stable",". The 0.5 s LCP improvement came from lazy-loading below-the-fold images, which is a separate lever — and one that is only safe once the boxes are reserved.",[36,1445,1447],{"id":1446},"pitfalls-rollback","Pitfalls & Rollback",[41,1449,1450,1461,1474,1483,1489,1502],{},[44,1451,1452,1456,1457,1460],{},[1453,1454,1455],"strong",{},"Putting rendered size in the attributes."," Use intrinsic dimensions. Attributes of ",[21,1458,1459],{},"width=\"640\" height=\"360\""," on a 1600×900 source still work (same ratio), but a mismatched ratio distorts the image.",[44,1462,1463,1469,1470,1473],{},[1453,1464,1465,1466,1468],{},"Omitting ",[21,1467,348],{}," in CSS."," With ",[21,1471,1472],{},"max-width: 100%"," alone the browser honours the literal height attribute and squashes the picture on narrow viewports.",[44,1475,1476,1479,1480,1482],{},[1453,1477,1478],{},"One ratio for art-directed sources."," If crops differ per breakpoint, each ",[21,1481,473],{}," needs its own dimensions or the box re-reserves at the breakpoint.",[44,1484,1485,1488],{},[1453,1486,1487],{},"Reserving with a spacer element."," A JavaScript-inserted spacer runs after paint, which is the thing you are trying to avoid. Reserve in HTML and CSS only.",[44,1490,1491,1494,1495,1497,1498,34],{},[1453,1492,1493],{},"Lazy-loading the LCP image."," Correct dimensions make lazy loading safe for CLS but ",[21,1496,1337],{}," on the hero still delays LCP — keep the first image eager, as covered in ",[30,1499,1501],{"href":1500},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites\u002Freducing-lcp-from-hero-images-on-static-sites\u002F","Reducing LCP From Hero Images on Static Sites",[44,1503,1504,1507],{},[1453,1505,1506],{},"Rollback:"," every change here is additive markup. Removing the attributes or the wrapper class restores the previous behaviour with no data migration and no cache to purge.",[36,1509,1511],{"id":1510},"conclusion","Conclusion",[14,1513,1514,1515,76,1517,1519,1520,1522,1523,34],{},"Reserving space is the cheapest performance work available to a static site: no new bytes, no new requests, and a build-time source of truth for every dimension. Emit ",[21,1516,75],{},[21,1518,79],{}," on every image, give each ",[21,1521,473],{}," its own pair when crops differ, wrap embeds in an aspect-ratio box, and enforce it with a build check so the next contributor cannot regress it. Then move on to the remaining causes — fonts and injected content — in ",[30,1524,33],{"href":32},[36,1526,1528],{"id":1527},"faq","FAQ",[215,1530,1532],{"id":1531},"do-i-still-need-width-and-height-if-my-css-sets-aspect-ratio","Do I still need width and height if my CSS sets aspect-ratio?",[14,1534,1535,1536,1538],{},"Yes. The HTML attributes are available to the browser before any stylesheet is parsed, so they protect the very first layout pass. The CSS ",[21,1537,83],{}," property is a safety net for cases where a responsive rule changes the rendered width, not a replacement for the attributes.",[215,1540,1542],{"id":1541},"what-values-go-in-width-and-height-for-a-responsive-image","What values go in width and height for a responsive image?",[14,1544,1545],{},"The intrinsic pixel dimensions of the source file, not the rendered size. The browser only uses the ratio between them, then scales the box to whatever CSS says the width should be, so a 1600 by 900 attribute pair works perfectly for an image displayed at 640 pixels wide.",[215,1547,1549],{"id":1548},"how-do-i-reserve-space-for-an-iframe-whose-height-is-unknown","How do I reserve space for an iframe whose height is unknown?",[14,1551,1552,1553,1555],{},"Give the wrapper a fixed aspect ratio if the content has one, such as 16 by 9 for video, and a ",[21,1554,723],{}," otherwise. A slightly wrong reservation still scores far better than none, because the shift is limited to the difference rather than the whole element height.",[215,1557,1559],{"id":1558},"does-lazy-loading-cause-layout-shift","Does lazy loading cause layout shift?",[14,1561,1562,1563,1565],{},"Only if the element has no reserved space. With correct dimensions, ",[21,1564,1337],{}," is safe and saves bytes. Without them, lazy loading makes shifts worse because the image arrives later, after the reader has probably already started reading.",[215,1567,1569],{"id":1568},"what-about-images-whose-size-is-only-known-at-request-time","What about images whose size is only known at request time?",[14,1571,1572],{},"Fetch them at build time and record the dimensions in your content data, or serve them through a transform that guarantees a fixed ratio. If neither is possible, wrap them in a box with a declared ratio and accept letterboxing rather than a shift.",[36,1574,1576],{"id":1575},"related","Related",[41,1578,1579,1588,1595,1602,1607],{},[44,1580,1581,1584,1585,1587],{},[1453,1582,1583],{},"Parent:"," ",[30,1586,33],{"href":32}," — the other three causes of shift.",[44,1589,1590,1594],{},[30,1591,1593],{"href":1592},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds\u002F","Fixing CLS From Late-Loading Embeds"," — placeholders for content scripts inject.",[44,1596,1597,1601],{},[30,1598,1600],{"href":1599},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts\u002F","Eliminating Layout Shift From Web Fonts"," — the shift that survives after the images are fixed.",[44,1603,1604,1606],{},[30,1605,458],{"href":457}," — where the dimensions come from for free.",[44,1608,1609,1611],{},[30,1610,1501],{"href":1500}," — keeping the first image fast as well as stable.",[1613,1614,1615],"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 .szBVR, html code.shiki .szBVR{--shiki-default:#D73A49;--shiki-dark:#F97583}html pre.shiki code .sJ8bj, html code.shiki .sJ8bj{--shiki-default:#6A737D;--shiki-dark:#6A737D}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}",{"title":228,"searchDepth":272,"depth":272,"links":1617},[1618,1619,1620,1627,1628,1629,1630,1637],{"id":38,"depth":272,"text":39},{"id":68,"depth":272,"text":69},{"id":212,"depth":272,"text":213,"children":1621},[1622,1623,1624,1625,1626],{"id":217,"depth":395,"text":218},{"id":358,"depth":395,"text":359},{"id":466,"depth":395,"text":467},{"id":607,"depth":395,"text":608},{"id":873,"depth":395,"text":874},{"id":1255,"depth":272,"text":1256},{"id":1446,"depth":272,"text":1447},{"id":1510,"depth":272,"text":1511},{"id":1527,"depth":272,"text":1528,"children":1631},[1632,1633,1634,1635,1636],{"id":1531,"depth":395,"text":1532},{"id":1541,"depth":395,"text":1542},{"id":1548,"depth":395,"text":1549},{"id":1558,"depth":395,"text":1559},{"id":1568,"depth":395,"text":1569},{"id":1575,"depth":272,"text":1576},[1639,1641,1644,1645],{"name":1640,"item":367},"Home",{"name":1642,"item":1643},"Performance Optimization & Core Web Vitals for SSGs","\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002F",{"name":33,"item":32},{"name":5,"item":1646},"\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","2026-08-01","Stamp intrinsic dimensions on every image, wrap iframes in an aspect-ratio box, and handle art-directed sources — the build-time recipe that takes body-image CLS from 0.31 to 0.01.","md",[1651,1653,1654,1656,1658],{"q":1532,"a":1652},"Yes. The HTML attributes are available to the browser before any stylesheet is parsed, so they protect the very first layout pass. The CSS aspect-ratio property is a safety net for cases where a responsive rule changes the rendered width, not a replacement for the attributes.",{"q":1542,"a":1545},{"q":1549,"a":1655},"Give the wrapper a fixed aspect ratio if the content has one, such as 16 by 9 for video, and a min-height otherwise. A slightly wrong reservation still scores far better than none, because the shift is limited to the difference rather than the whole element height.",{"q":1559,"a":1657},"Only if the element has no reserved space. With correct dimensions, loading equals lazy is safe and saves bytes. Without them, lazy loading makes shifts worse because the image arrives later, after the reader has probably already started reading.",{"q":1569,"a":1572},{},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift",{"title":5,"description":1648},"reserving-space-for-images-and-embeds-to-stop-layout-shift","performance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift\u002Findex","article","ZuA98aN-GPmqA38oGU5PKHPIciu3GlX1qlNuxcHTIzA",[1667,1670,1673,1676,1679,1682,1685,1688,1691,1694,1697,1700,1703,1706,1709,1712,1715,1718,1721,1724,1727,1730,1733,1736,1739,1742,1745,1748,1751,1754,1757,1759,1761,1763,1766,1767,1770,1773,1776,1779,1781,1783,1786,1789,1792,1795,1798,1801,1804,1807,1810,1813,1816,1819,1822,1825,1828,1831,1834,1837,1840,1843,1846,1849,1852,1855,1857,1860,1863,1866,1869,1872,1875,1878,1881,1884,1887,1890,1893,1896,1899,1902,1905],{"path":1668,"title":1669},"\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":1671,"title":1672},"\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":1674,"title":1675},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1677,"title":1678},"\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":1680,"title":1681},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1683,"title":1684},"\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":1686,"title":1687},"\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":1689,"title":1690},"\u002Fchoosing-the-right-static-site-generator-for-production","Choosing the Right Static Site Generator for Production",{"path":1692,"title":1693},"\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":1695,"title":1696},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1698,"title":1699},"\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":1701,"title":1702},"\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":1704,"title":1705},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1707,"title":1708},"\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":1710,"title":1711},"\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":1713,"title":1714},"\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":1716,"title":1717},"\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":1719,"title":1720},"\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":1722,"title":1723},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites","Next.js Static Export for Content Sites",{"path":1725,"title":1726},"\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":1728,"title":1729},"\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":1731,"title":1732},"\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":1734,"title":1735},"\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":1737,"title":1738},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1740,"title":1741},"\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":1743,"title":1744},"\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":1746,"title":1747},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1749,"title":1750},"\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":1752,"title":1753},"\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":1755,"title":1756},"\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":1758,"title":1600},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Feliminating-layout-shift-from-web-fonts",{"path":1760,"title":1593},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds",{"path":1762,"title":33},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites",{"path":1764,"title":1765},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Fmeasuring-cls-in-the-field-with-web-vitals-js","Measuring CLS in the Field With web-vitals.js",{"path":1660,"title":5},{"path":1768,"title":1769},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1771,"title":1772},"\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":1774,"title":1775},"\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":1777,"title":1778},"\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":1780,"title":458},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro",{"path":1782,"title":463},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro\u002Foptimizing-webp-images-in-hugo-without-plugins",{"path":1784,"title":1785},"\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":1787,"title":1788},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1790,"title":1791},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1793,"title":1794},"\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":1796,"title":1797},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering","JavaScript Hydration & Partial Rendering",{"path":1799,"title":1800},"\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":1802,"title":1803},"\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":1805,"title":1806},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1808,"title":1809},"\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":1811,"title":1812},"\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":1814,"title":1815},"\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":1817,"title":1818},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1820,"title":1821},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites\u002Flazy-loading-youtube-embeds-on-static-sites","Lazy-Loading YouTube Embeds on Static Sites",{"path":1823,"title":1824},"\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":1826,"title":1827},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1829,"title":1830},"\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":1832,"title":1833},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":1835,"title":1836},"\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":1838,"title":1839},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":1841,"title":1842},"\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":1844,"title":1845},"\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":1847,"title":1848},"\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":1850,"title":1851},"\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":1853,"title":1854},"\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":1856,"title":1252},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds",{"path":1858,"title":1859},"\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":1861,"title":1862},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1864,"title":1865},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":1867,"title":1868},"\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":1870,"title":1871},"\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":1873,"title":1874},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1876,"title":1877},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":1879,"title":1880},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":1882,"title":1883},"\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":1885,"title":1886},"\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":1888,"title":1889},"\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":1891,"title":1892},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":1894,"title":1895},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":1897,"title":1898},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":1900,"title":1901},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":1903,"title":1904},"\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":1906,"title":1907},"\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",1785611671166]