[{"data":1,"prerenderedAt":1796},["ShallowReactive",2],{"page:\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export":3,"all-docs-nav":1553},{"id":4,"title":5,"body":6,"breadcrumb":1528,"dateModified":1537,"datePublished":1537,"description":1538,"extension":255,"faq":1539,"meta":1548,"navigation":127,"path":1549,"seo":1550,"slug":12,"stem":1551,"type":434,"__hash__":1552},"content\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export\u002Findex.md","Handling Dynamic Routes in Next.js Static Export",{"type":7,"value":8,"toc":1509},"minimark",[9,13,21,34,39,58,62,68,508,514,645,649,652,804,827,831,838,905,924,985,992,996,999,1030,1113,1217,1221,1224,1234,1240,1251,1255,1258,1330,1337,1341,1393,1397,1408,1412,1417,1423,1427,1433,1437,1451,1455,1458,1462,1465,1469,1505],[10,11,5],"h1",{"id":12},"handling-dynamic-routes-in-nextjs-static-export",[14,15,16,20],"p",{},[17,18,19],"code",{},"output: 'export'"," turns a Next.js application into a directory of HTML files. For a content site that is exactly what you want — no server, no runtime cost, deployable anywhere. The friction is that Next's routing was designed with a server available, and under export several conveniences disappear at once: no fallback rendering, no middleware, no on-demand revalidation.",[14,22,23,24,27,28,33],{},"Dynamic routes still work, and they work well, provided you accept that the list of pages must be fully known at build time. This guide covers ",[17,25,26],{},"generateStaticParams",", catch-all segments for documentation trees, the trailing-slash trap, and what to do about the routes you cannot enumerate. It is part of ",[29,30,32],"a",{"href":31},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002F","Next.js Static Export for Content Sites",".",[35,36,38],"h2",{"id":37},"prerequisites","Prerequisites",[40,41,42,52,55],"ul",{},[43,44,45,46,48,49,33],"li",{},"Next.js 14 or newer using the App Router, with ",[17,47,19],{}," in ",[17,50,51],{},"next.config.js",[43,53,54],{},"Content whose full set of routes can be enumerated at build time — files on disk, a committed data file, or an API you can query during the build.",[43,56,57],{},"A host whose trailing-slash behaviour you can check or configure.",[35,59,61],{"id":60},"enumerate-every-route","Enumerate Every Route",[14,63,64,65,67],{},"Under export, ",[17,66,26],{}," is not an optimisation — it is the definition of which pages exist:",[69,70,75],"pre",{"className":71,"code":72,"language":73,"meta":74,"style":74},"language-typescript shiki shiki-themes github-light github-dark","\u002F\u002F app\u002Fguides\u002F[slug]\u002Fpage.tsx\nimport { readdir, readFile } from 'node:fs\u002Fpromises';\nimport matter from 'gray-matter';\n\nexport async function generateStaticParams() {\n  const files = await readdir('content\u002Fguides');\n  return files\n    .filter((f) => f.endsWith('.md'))\n    .map((f) => ({ slug: f.replace(\u002F\\.md$\u002F, '') }));\n}\n\nexport const dynamicParams = false;   \u002F\u002F anything not listed is a build error, not a 404\n\nexport default async function GuidePage({ params }: { params: { slug: string } }) {\n  const raw = await readFile(`content\u002Fguides\u002F${params.slug}.md`, 'utf8');\n  const { data, content } = matter(raw);\n  return (\n    \u003Carticle>\n      \u003Ch1>{data.title}\u003C\u002Fh1>\n      \u003Cdiv dangerouslySetInnerHTML={{ __html: await render(content) }} \u002F>\n    \u003C\u002Farticle>\n  );\n}\n","typescript","",[17,76,77,86,107,122,129,148,176,185,221,270,276,281,303,308,355,391,418,426,438,456,487,497,503],{"__ignoreMap":74},[78,79,82],"span",{"class":80,"line":81},"line",1,[78,83,85],{"class":84},"sJ8bj","\u002F\u002F app\u002Fguides\u002F[slug]\u002Fpage.tsx\n",[78,87,89,93,97,100,104],{"class":80,"line":88},2,[78,90,92],{"class":91},"szBVR","import",[78,94,96],{"class":95},"sVt8B"," { readdir, readFile } ",[78,98,99],{"class":91},"from",[78,101,103],{"class":102},"sZZnC"," 'node:fs\u002Fpromises'",[78,105,106],{"class":95},";\n",[78,108,110,112,115,117,120],{"class":80,"line":109},3,[78,111,92],{"class":91},[78,113,114],{"class":95}," matter ",[78,116,99],{"class":91},[78,118,119],{"class":102}," 'gray-matter'",[78,121,106],{"class":95},[78,123,125],{"class":80,"line":124},4,[78,126,128],{"emptyLinePlaceholder":127},true,"\n",[78,130,132,135,138,141,145],{"class":80,"line":131},5,[78,133,134],{"class":91},"export",[78,136,137],{"class":91}," async",[78,139,140],{"class":91}," function",[78,142,144],{"class":143},"sScJk"," generateStaticParams",[78,146,147],{"class":95},"() {\n",[78,149,151,154,158,161,164,167,170,173],{"class":80,"line":150},6,[78,152,153],{"class":91},"  const",[78,155,157],{"class":156},"sj4cs"," files",[78,159,160],{"class":91}," =",[78,162,163],{"class":91}," await",[78,165,166],{"class":143}," readdir",[78,168,169],{"class":95},"(",[78,171,172],{"class":102},"'content\u002Fguides'",[78,174,175],{"class":95},");\n",[78,177,179,182],{"class":80,"line":178},7,[78,180,181],{"class":91},"  return",[78,183,184],{"class":95}," files\n",[78,186,188,191,194,197,201,204,207,210,213,215,218],{"class":80,"line":187},8,[78,189,190],{"class":95},"    .",[78,192,193],{"class":143},"filter",[78,195,196],{"class":95},"((",[78,198,200],{"class":199},"s4XuR","f",[78,202,203],{"class":95},") ",[78,205,206],{"class":91},"=>",[78,208,209],{"class":95}," f.",[78,211,212],{"class":143},"endsWith",[78,214,169],{"class":95},[78,216,217],{"class":102},"'.md'",[78,219,220],{"class":95},"))\n",[78,222,224,226,229,231,233,235,237,240,243,245,248,252,256,259,261,264,267],{"class":80,"line":223},9,[78,225,190],{"class":95},[78,227,228],{"class":143},"map",[78,230,196],{"class":95},[78,232,200],{"class":199},[78,234,203],{"class":95},[78,236,206],{"class":91},[78,238,239],{"class":95}," ({ slug: f.",[78,241,242],{"class":143},"replace",[78,244,169],{"class":95},[78,246,247],{"class":102},"\u002F",[78,249,251],{"class":250},"snhLl","\\.",[78,253,255],{"class":254},"sA_wV","md",[78,257,258],{"class":91},"$",[78,260,247],{"class":102},[78,262,263],{"class":95},", ",[78,265,266],{"class":102},"''",[78,268,269],{"class":95},") }));\n",[78,271,273],{"class":80,"line":272},10,[78,274,275],{"class":95},"}\n",[78,277,279],{"class":80,"line":278},11,[78,280,128],{"emptyLinePlaceholder":127},[78,282,284,286,289,292,294,297,300],{"class":80,"line":283},12,[78,285,134],{"class":91},[78,287,288],{"class":91}," const",[78,290,291],{"class":156}," dynamicParams",[78,293,160],{"class":91},[78,295,296],{"class":156}," false",[78,298,299],{"class":95},";   ",[78,301,302],{"class":84},"\u002F\u002F anything not listed is a build error, not a 404\n",[78,304,306],{"class":80,"line":305},13,[78,307,128],{"emptyLinePlaceholder":127},[78,309,311,313,316,318,320,323,326,329,332,335,338,340,342,344,347,349,352],{"class":80,"line":310},14,[78,312,134],{"class":91},[78,314,315],{"class":91}," default",[78,317,137],{"class":91},[78,319,140],{"class":91},[78,321,322],{"class":143}," GuidePage",[78,324,325],{"class":95},"({ ",[78,327,328],{"class":199},"params",[78,330,331],{"class":95}," }",[78,333,334],{"class":91},":",[78,336,337],{"class":95}," { ",[78,339,328],{"class":199},[78,341,334],{"class":91},[78,343,337],{"class":95},[78,345,346],{"class":199},"slug",[78,348,334],{"class":91},[78,350,351],{"class":156}," string",[78,353,354],{"class":95}," } }) {\n",[78,356,358,360,363,365,367,370,372,375,377,379,381,384,386,389],{"class":80,"line":357},15,[78,359,153],{"class":91},[78,361,362],{"class":156}," raw",[78,364,160],{"class":91},[78,366,163],{"class":91},[78,368,369],{"class":143}," readFile",[78,371,169],{"class":95},[78,373,374],{"class":102},"`content\u002Fguides\u002F${",[78,376,328],{"class":95},[78,378,33],{"class":102},[78,380,346],{"class":95},[78,382,383],{"class":102},"}.md`",[78,385,263],{"class":95},[78,387,388],{"class":102},"'utf8'",[78,390,175],{"class":95},[78,392,394,396,398,401,403,406,409,412,415],{"class":80,"line":393},16,[78,395,153],{"class":91},[78,397,337],{"class":95},[78,399,400],{"class":156},"data",[78,402,263],{"class":95},[78,404,405],{"class":156},"content",[78,407,408],{"class":95}," } ",[78,410,411],{"class":91},"=",[78,413,414],{"class":143}," matter",[78,416,417],{"class":95},"(raw);\n",[78,419,421,423],{"class":80,"line":420},17,[78,422,181],{"class":91},[78,424,425],{"class":95}," (\n",[78,427,429,432,435],{"class":80,"line":428},18,[78,430,431],{"class":95},"    \u003C",[78,433,434],{"class":143},"article",[78,436,437],{"class":95},">\n",[78,439,441,444,446,449,452,454],{"class":80,"line":440},19,[78,442,443],{"class":95},"      \u003C",[78,445,10],{"class":143},[78,447,448],{"class":95},">{data.title}",[78,450,451],{"class":91},"\u003C\u002F",[78,453,10],{"class":95},[78,455,437],{"class":91},[78,457,459,461,464,466,469,472,475,478,481,484],{"class":80,"line":458},20,[78,460,443],{"class":91},[78,462,463],{"class":95},"div dangerouslySetInnerHTML",[78,465,411],{"class":91},[78,467,468],{"class":95},"{{ ",[78,470,471],{"class":143},"__html",[78,473,474],{"class":95},": ",[78,476,477],{"class":91},"await",[78,479,480],{"class":143}," render",[78,482,483],{"class":95},"(content) }} ",[78,485,486],{"class":91},"\u002F>\n",[78,488,490,493,495],{"class":80,"line":489},21,[78,491,492],{"class":91},"    \u003C\u002F",[78,494,434],{"class":95},[78,496,437],{"class":91},[78,498,500],{"class":80,"line":499},22,[78,501,502],{"class":95},"  );\n",[78,504,506],{"class":80,"line":505},23,[78,507,275],{"class":95},[14,509,510,513],{},[17,511,512],{},"dynamicParams = false"," is worth setting explicitly. It converts \"a link points at a page we never generated\" from a silent 404 in production into a build error, which is the failure you want.",[515,516,517,641],"figure",{},[518,519,526,527,526,531,526,535,526,542],"svg",{"viewBox":520,"role":521,"ariaLabelledBy":522,"xmlns":525},"0 0 760 300","img",[523,524],"nx-routes-title","nx-routes-desc","http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","\n  ",[528,529,530],"title",{"id":523},"What exists under static export",[532,533,534],"desc",{"id":524},"A dynamic route with generateStaticParams returning three slugs produces exactly three HTML files. A fourth slug that was not returned produces no file, so the host serves a 404. With a server, that fourth request could have been rendered on demand; under export there is nothing to render it.",[536,537],"rect",{"x":538,"y":538,"width":539,"height":540,"fill":541},"0","760","300","#ffffff",[543,544,546,547,546,555,546,565,546,570,546,576,546,580,546,584,546,591,546,595,546,599,546,602,546,605,546,612,546,616,546,620,546,625,546,632,546,637,526],"g",{"style":545},"font-family:system-ui, sans-serif;font-size:12px","\n    ",[548,549,554],"text",{"x":550,"y":551,"fill":552,"style":553},"380","28","#1f2937","font-size:16px;font-weight:700;text-anchor:middle","The params function is the site map",[536,556],{"x":557,"y":558,"width":559,"height":560,"rx":561,"fill":562,"opacity":563,"stroke":562,"style":564},"40","60","200","120","12","#6a4c93","0.14","stroke-width:1.5px",[548,566,26],{"x":567,"y":568,"fill":552,"style":569},"140","86","font-size:12px;font-weight:700;text-anchor:middle",[548,571,575],{"x":572,"y":573,"fill":552,"style":574},"62","114","font-size:12px","deploying",[548,577,579],{"x":572,"y":578,"fill":552,"style":574},"138","caching",[548,581,583],{"x":572,"y":582,"fill":552,"style":574},"162","previews",[536,585],{"x":586,"y":558,"width":587,"height":560,"rx":561,"fill":588,"opacity":589,"stroke":590,"style":564},"290","220","#8ac926","0.16","#5a8a16",[548,592,594],{"x":593,"y":568,"fill":552,"style":569},"400","exported files",[548,596,598],{"x":597,"y":573,"fill":552,"style":574},"312","guides\u002Fdeploying\u002Findex.html",[548,600,601],{"x":597,"y":578,"fill":552,"style":574},"guides\u002Fcaching\u002Findex.html",[548,603,604],{"x":597,"y":582,"fill":552,"style":574},"guides\u002Fpreviews\u002Findex.html",[536,606],{"x":607,"y":558,"width":608,"height":560,"rx":561,"fill":609,"opacity":610,"stroke":611,"style":564},"560","160","#d9e2ef","0.5","#556071",[548,613,615],{"x":614,"y":568,"fill":552,"style":569},"640","served",[548,617,619],{"x":614,"y":573,"fill":590,"style":618},"font-size:12px;text-anchor:middle","200 · 200 · 200",[548,621,624],{"x":614,"y":622,"fill":611,"style":623},"150","font-size:11px;text-anchor:middle","from any static host",[536,626],{"x":557,"y":627,"width":628,"height":572,"rx":561,"fill":629,"opacity":630,"stroke":631,"style":564},"204","680","#ff595e","0.12","#d83b41",[548,633,636],{"x":550,"y":634,"fill":552,"style":635},"228","font-size:13px;font-weight:700;text-anchor:middle","\u002Fguides\u002Frollbacks\u002F — not returned by the params function",[548,638,640],{"x":550,"y":639,"fill":552,"style":618},"250","no file exported → 404, with no server available to render it on demand",[642,643,644],"figcaption",{},"With a server, an unlisted slug can be rendered on request. Under export there is no such path — which is why `dynamicParams = false` and a link check are the two things that make this target safe.",[35,646,648],{"id":647},"catch-all-segments-for-documentation-trees","Catch-All Segments for Documentation Trees",[14,650,651],{},"Documentation is rarely flat. A catch-all segment plus a directory walk produces one page per document at whatever depth the content sits:",[69,653,655],{"className":71,"code":654,"language":73,"meta":74,"style":74},"\u002F\u002F app\u002Fdocs\u002F[...path]\u002Fpage.tsx\nimport { globSync } from 'node:fs';\nimport path from 'node:path';\n\nexport const dynamicParams = false;\n\nexport async function generateStaticParams() {\n  return globSync('content\u002Fdocs\u002F**\u002F*.md').map((file) => ({\n    path: path.relative('content\u002Fdocs', file).replace(\u002F\\.md$\u002F, '').split(path.sep),\n  }));\n}\n",[17,656,657,662,676,690,694,708,712,724,753,795,800],{"__ignoreMap":74},[78,658,659],{"class":80,"line":81},[78,660,661],{"class":84},"\u002F\u002F app\u002Fdocs\u002F[...path]\u002Fpage.tsx\n",[78,663,664,666,669,671,674],{"class":80,"line":88},[78,665,92],{"class":91},[78,667,668],{"class":95}," { globSync } ",[78,670,99],{"class":91},[78,672,673],{"class":102}," 'node:fs'",[78,675,106],{"class":95},[78,677,678,680,683,685,688],{"class":80,"line":109},[78,679,92],{"class":91},[78,681,682],{"class":95}," path ",[78,684,99],{"class":91},[78,686,687],{"class":102}," 'node:path'",[78,689,106],{"class":95},[78,691,692],{"class":80,"line":124},[78,693,128],{"emptyLinePlaceholder":127},[78,695,696,698,700,702,704,706],{"class":80,"line":131},[78,697,134],{"class":91},[78,699,288],{"class":91},[78,701,291],{"class":156},[78,703,160],{"class":91},[78,705,296],{"class":156},[78,707,106],{"class":95},[78,709,710],{"class":80,"line":150},[78,711,128],{"emptyLinePlaceholder":127},[78,713,714,716,718,720,722],{"class":80,"line":178},[78,715,134],{"class":91},[78,717,137],{"class":91},[78,719,140],{"class":91},[78,721,144],{"class":143},[78,723,147],{"class":95},[78,725,726,728,731,733,736,739,741,743,746,748,750],{"class":80,"line":187},[78,727,181],{"class":91},[78,729,730],{"class":143}," globSync",[78,732,169],{"class":95},[78,734,735],{"class":102},"'content\u002Fdocs\u002F**\u002F*.md'",[78,737,738],{"class":95},").",[78,740,228],{"class":143},[78,742,196],{"class":95},[78,744,745],{"class":199},"file",[78,747,203],{"class":95},[78,749,206],{"class":91},[78,751,752],{"class":95}," ({\n",[78,754,755,758,761,763,766,769,771,773,775,777,779,781,783,785,787,789,792],{"class":80,"line":223},[78,756,757],{"class":95},"    path: path.",[78,759,760],{"class":143},"relative",[78,762,169],{"class":95},[78,764,765],{"class":102},"'content\u002Fdocs'",[78,767,768],{"class":95},", file).",[78,770,242],{"class":143},[78,772,169],{"class":95},[78,774,247],{"class":102},[78,776,251],{"class":250},[78,778,255],{"class":254},[78,780,258],{"class":91},[78,782,247],{"class":102},[78,784,263],{"class":95},[78,786,266],{"class":102},[78,788,738],{"class":95},[78,790,791],{"class":143},"split",[78,793,794],{"class":95},"(path.sep),\n",[78,796,797],{"class":80,"line":272},[78,798,799],{"class":95},"  }));\n",[78,801,802],{"class":80,"line":278},[78,803,275],{"class":95},[14,805,806,807,810,811,814,815,818,819,822,823,826],{},"The ",[17,808,809],{},"path"," param arrives as an array of segments, so ",[17,812,813],{},"content\u002Fdocs\u002Fdeploy\u002Fedge\u002Fworkers.md"," becomes ",[17,816,817],{},"\u002Fdocs\u002Fdeploy\u002Fedge\u002Fworkers\u002F",". Two practical notes: use ",[17,820,821],{},"path.sep"," rather than a hard-coded slash so the build works on Windows, and exclude ",[17,824,825],{},"index.md"," files from the glob if you want a section landing page handled by a separate route.",[35,828,830],{"id":829},"get-trailing-slashes-right-once","Get Trailing Slashes Right Once",[14,832,833,834,837],{},"This is the most common source of \"it works locally and 404s in production\" with static export. The ",[17,835,836],{},"trailingSlash"," option decides what the exporter writes:",[69,839,843],{"className":840,"code":841,"language":842,"meta":74,"style":74},"language-javascript shiki shiki-themes github-light github-dark","\u002F\u002F next.config.js\nmodule.exports = {\n  output: 'export',\n  trailingSlash: true,      \u002F\u002F writes guides\u002Fdeploying\u002Findex.html\n  images: { unoptimized: true },\n};\n","javascript",[17,844,845,850,865,876,890,900],{"__ignoreMap":74},[78,846,847],{"class":80,"line":81},[78,848,849],{"class":84},"\u002F\u002F next.config.js\n",[78,851,852,855,857,860,862],{"class":80,"line":88},[78,853,854],{"class":156},"module",[78,856,33],{"class":95},[78,858,859],{"class":156},"exports",[78,861,160],{"class":91},[78,863,864],{"class":95}," {\n",[78,866,867,870,873],{"class":80,"line":109},[78,868,869],{"class":95},"  output: ",[78,871,872],{"class":102},"'export'",[78,874,875],{"class":95},",\n",[78,877,878,881,884,887],{"class":80,"line":124},[78,879,880],{"class":95},"  trailingSlash: ",[78,882,883],{"class":156},"true",[78,885,886],{"class":95},",      ",[78,888,889],{"class":84},"\u002F\u002F writes guides\u002Fdeploying\u002Findex.html\n",[78,891,892,895,897],{"class":80,"line":131},[78,893,894],{"class":95},"  images: { unoptimized: ",[78,896,883],{"class":156},[78,898,899],{"class":95}," },\n",[78,901,902],{"class":80,"line":150},[78,903,904],{"class":95},"};\n",[14,906,907,908,911,912,915,916,919,920,923],{},"With ",[17,909,910],{},"trailingSlash: true",", every route becomes a directory with an ",[17,913,914],{},"index.html"," — which is what most static hosts serve naturally for a path ending in a slash. With it false, you get ",[17,917,918],{},"guides\u002Fdeploying.html",", and whether ",[17,921,922],{},"\u002Fguides\u002Fdeploying\u002F"," resolves depends entirely on the host.",[925,926,927,949],"table",{},[928,929,930],"thead",{},[931,932,933,937,940,944],"tr",{},[934,935,936],"th",{},"Setting",[934,938,939],{},"Exported file",[934,941,942],{},[17,943,922],{},[934,945,946],{},[17,947,948],{},"\u002Fguides\u002Fdeploying",[950,951,952,968],"tbody",{},[931,953,954,959,963,965],{},[955,956,957],"td",{},[17,958,910],{},[955,960,961],{},[17,962,598],{},[955,964,559],{},[955,966,967],{},"redirect (host-dependent)",[931,969,970,975,979,982],{},[955,971,972],{},[17,973,974],{},"trailingSlash: false",[955,976,977],{},[17,978,918],{},[955,980,981],{},"404 or redirect",[955,983,984],{},"200 (host-dependent)",[14,986,987,988,991],{},"Pick one, make every internal link match it, and verify against a real deployment rather than ",[17,989,990],{},"next start",". A mismatch does not break the site — it turns every internal link into a redirect, which is a measurable delay on a page with forty of them.",[35,993,995],{"id":994},"what-you-give-up","What You Give Up",[14,997,998],{},"Under export, several Next features are simply unavailable, and it is better to know before choosing the target:",[40,1000,1001,1008,1014,1024],{},[43,1002,1003,1007],{},[1004,1005,1006],"strong",{},"Middleware"," — no request-time code, so no redirects, rewrites, geolocation or auth at the edge from Next itself. Your host's own edge layer can still do all of it.",[43,1009,1010,1013],{},[1004,1011,1012],{},"Route handlers"," — no API routes in the same project; the client calls an external API directly.",[43,1015,1016,1019,1020,1023],{},[1004,1017,1018],{},"Image optimisation"," — the built-in optimizer needs a server, so ",[17,1021,1022],{},"images.unoptimized"," is required or a custom loader points at an image CDN.",[43,1025,1026,1029],{},[1004,1027,1028],{},"Revalidation"," — no ISR, no on-demand revalidation. Fresh data means a rebuild or a client-side fetch.",[515,1031,1032,1110],{},[518,1033,526,1038,526,1041,526,1044,526,1046],{"viewBox":1034,"role":521,"ariaLabelledBy":1035,"xmlns":525},"0 0 760 290",[1036,1037],"nx-gives-title","nx-gives-desc",[528,1039,1040],{"id":1036},"Which Next features survive static export",[532,1042,1043],{"id":1037},"Two columns. Available under export: App Router, dynamic and catch-all routes, React Server Components rendered at build time, client components, and static asset handling. Unavailable: middleware, route handlers, built-in image optimisation, incremental static regeneration and on-demand revalidation.",[536,1045],{"x":538,"y":538,"width":539,"height":586,"fill":541},[543,1047,546,1048,546,1051,546,1056,546,1061,546,1065,546,1069,546,1072,546,1076,546,1080,546,1085,546,1088,546,1091,546,1095,546,1098,546,1101,546,1104,546,1107,526],{"style":545},[548,1049,1050],{"x":550,"y":551,"fill":552,"style":553},"Check this list before choosing the target",[536,1052],{"x":557,"y":1053,"width":1054,"height":1055,"rx":561,"fill":588,"opacity":563,"stroke":590,"style":564},"56","330","190",[548,1057,1060],{"x":1058,"y":1059,"fill":552,"style":635},"205","82","Works under export",[548,1062,1064],{"x":572,"y":1063,"fill":552,"style":574},"112","App Router and layouts",[548,1066,1068],{"x":572,"y":1067,"fill":552,"style":574},"136","dynamic + catch-all routes",[548,1070,1071],{"x":572,"y":608,"fill":552,"style":574},"server components at build time",[548,1073,1075],{"x":572,"y":1074,"fill":552,"style":574},"184","client components and hydration",[548,1077,1079],{"x":572,"y":1078,"fill":552,"style":574},"208","static assets and fonts",[548,1081,1084],{"x":572,"y":1082,"fill":611,"style":1083},"232","font-size:11px","everything a content site normally needs",[536,1086],{"x":593,"y":1053,"width":1087,"height":1055,"rx":561,"fill":629,"opacity":630,"stroke":631,"style":564},"320",[548,1089,1090],{"x":607,"y":1059,"fill":552,"style":635},"Unavailable",[548,1092,1094],{"x":1093,"y":1063,"fill":552,"style":574},"424","middleware",[548,1096,1097],{"x":1093,"y":1067,"fill":552,"style":574},"route handlers \u002F API routes",[548,1099,1100],{"x":1093,"y":608,"fill":552,"style":574},"built-in image optimisation",[548,1102,1103],{"x":1093,"y":1074,"fill":552,"style":574},"ISR and on-demand revalidation",[548,1105,1106],{"x":1093,"y":1078,"fill":552,"style":574},"server actions",[548,1108,1109],{"x":1093,"y":1082,"fill":611,"style":1083},"move these to the host's edge layer, or rebuild",[642,1111,1112],{},"Nothing in the right-hand column is missing from a static site in general — the host's edge layer provides most of it. What is missing is Next's own implementation of it.",[515,1114,1115,1214],{},[518,1116,526,1120,526,1123,526,1126,526,1128,526,1200],{"viewBox":1034,"role":521,"ariaLabelledBy":1117,"xmlns":525},[1118,1119],"nx-slash-title","nx-slash-desc",[528,1121,1122],{"id":1118},"How trailingSlash changes the exported file layout",[532,1124,1125],{"id":1119},"With trailingSlash true, the route slash guides slash deploying exports to guides slash deploying slash index dot html and resolves directly for a slashed URL. With trailingSlash false it exports to guides slash deploying dot html, and a slashed request depends on host normalisation, producing a redirect or a 404.",[536,1127],{"x":538,"y":538,"width":539,"height":586,"fill":541},[543,1129,546,1130,546,1133,546,1137,546,1143,546,1147,546,1151,546,1155,546,1159,546,1163,546,1168,546,1171,546,1173,546,1176,546,1180,546,1192,546,1196,526],{"style":545},[548,1131,1132],{"x":550,"y":551,"fill":552,"style":553},"One setting, two very different file trees",[548,1134,910],{"x":557,"y":1135,"fill":590,"style":1136},"76","font-size:12px;font-weight:700",[536,1138],{"x":559,"y":1053,"width":1139,"height":1140,"rx":1141,"fill":588,"opacity":1142,"stroke":590,"style":564},"230","44","8","0.18",[548,1144,598],{"x":1145,"y":1146,"fill":552,"style":618},"315","83",[536,1148],{"x":1149,"y":1053,"width":1150,"height":1140,"rx":1141,"fill":609,"opacity":610,"stroke":611,"style":564},"460","260",[548,1152,1154],{"x":1153,"y":1135,"fill":552,"style":618},"590","\u002Fguides\u002Fdeploying\u002F → 200",[548,1156,1158],{"x":1153,"y":1157,"fill":611,"style":623},"93","every host serves this the same way",[548,1160,974],{"x":557,"y":1161,"fill":1162,"style":1136},"176","#a97b00",[536,1164],{"x":559,"y":1165,"width":1139,"height":1140,"rx":1141,"fill":1166,"opacity":1167,"stroke":1162,"style":564},"156","#ffca3a","0.3",[548,1169,918],{"x":1145,"y":1170,"fill":552,"style":618},"183",[536,1172],{"x":1149,"y":1165,"width":1150,"height":1140,"rx":1141,"fill":629,"opacity":630,"stroke":631,"style":564},[548,1174,1175],{"x":1153,"y":1161,"fill":552,"style":618},"\u002Fguides\u002Fdeploying\u002F → host-dependent",[548,1177,1179],{"x":1153,"y":1178,"fill":631,"style":623},"193","redirect on some hosts, 404 on others",[543,1181,1184,1185,1184,1189,546],{"stroke":611,"fill":1182,"style":1183},"none","stroke-width:2px","\n      ",[809,1186],{"d":1187,"style":1188},"M432 78 L458 78","marker-end:url(#nx-slash-arrow)",[809,1190],{"d":1191,"style":1188},"M432 178 L458 178",[548,1193,1195],{"x":557,"y":1194,"fill":611,"style":1083},"248","Whichever you choose, make internal links match it — a mismatch turns every link into a redirect",[548,1197,1199],{"x":557,"y":1198,"fill":611,"style":1083},"270","Verify on a real deployment; the local dev server normalises more forgivingly than most hosts",[1201,1202,546,1203,526],"defs",{},[1204,1205,1184,1211,546],"marker",{"id":1206,"viewBox":1207,"refX":1141,"refY":1208,"markerWidth":1209,"markerHeight":1209,"orient":1210},"nx-slash-arrow","0 0 10 10","5","7","auto-start-reverse",[809,1212],{"d":1213,"fill":611},"M0 0 L10 5 L0 10 z",[642,1215,1216],{},"The directory form is the safer default for a content site: it behaves identically across hosts and matches how documentation URLs are usually written.",[35,1218,1220],{"id":1219},"routes-you-cannot-enumerate","Routes You Cannot Enumerate",[14,1222,1223],{},"Occasionally a route genuinely cannot be listed at build time: a search results page, a permalink built from a query parameter, a preview of unpublished content. Three answers, in order of preference.",[14,1225,1226,1229,1230,1233],{},[1004,1227,1228],{},"Move the variation into query parameters on one static page."," ",[17,1231,1232],{},"\u002Fsearch\u002F?q=deploy"," is one exported page that reads the query string on the client. This is almost always the right answer for search.",[14,1235,1236,1239],{},[1004,1237,1238],{},"Render it at the edge."," Your host's Worker or function layer can serve a route Next never exported — the two coexist happily, since the export is just files.",[14,1241,1242,1245,1246,1250],{},[1004,1243,1244],{},"Rebuild."," If the set of routes changes rarely, a rebuild triggered by the change is simpler than any runtime mechanism. The scheduled-build pattern in ",[29,1247,1249],{"href":1248},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds\u002F","Scheduling Content Publication With Cron-Triggered Builds"," covers this exactly.",[35,1252,1254],{"id":1253},"measured-impact","Measured Impact",[14,1256,1257],{},"A 1,400-page documentation site migrated from a Next server deployment to static export:",[925,1259,1260,1273],{},[928,1261,1262],{},[931,1263,1264,1267,1270],{},[934,1265,1266],{},"Measure",[934,1268,1269],{},"Server deployment",[934,1271,1272],{},"Static export",[950,1274,1275,1286,1297,1308,1319],{},[931,1276,1277,1280,1283],{},[955,1278,1279],{},"Build time",[955,1281,1282],{},"96 s",[955,1284,1285],{},"132 s",[931,1287,1288,1291,1294],{},[955,1289,1290],{},"p95 TTFB",[955,1292,1293],{},"210 ms",[955,1295,1296],{},"28 ms (edge cache)",[931,1298,1299,1302,1305],{},[955,1300,1301],{},"Hosting cost",[955,1303,1304],{},"per-request",[955,1306,1307],{},"flat, effectively zero",[931,1309,1310,1313,1316],{},[955,1311,1312],{},"Features lost",[955,1314,1315],{},"—",[955,1317,1318],{},"middleware, ISR, image optimizer",[931,1320,1321,1324,1327],{},[955,1322,1323],{},"Client JS (median page)",[955,1325,1326],{},"94 KB",[955,1328,1329],{},"94 KB (unchanged)",[14,1331,1332,1333,33],{},"The TTFB row is the point of the exercise: every page is a file at the edge, so there is no origin to wait for. The build-time row is the cost, and it is paid by CI rather than by readers. The client JavaScript is unchanged, which is worth noting — export removes the server, not the hydration, so a heavy component tree stays heavy. That side of the trade-off is covered in ",[29,1334,1336],{"href":1335},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002F","JavaScript Hydration & Partial Rendering",[35,1338,1340],{"id":1339},"pitfalls-rollback","Pitfalls & Rollback",[40,1342,1343,1353,1359,1369,1375,1381],{},[43,1344,1345,1352],{},[1004,1346,1347,1348,1351],{},"Leaving ",[17,1349,1350],{},"dynamicParams"," at its default."," A missing param becomes a production 404 instead of a build failure.",[43,1354,1355,1358],{},[1004,1356,1357],{},"Trailing-slash mismatch."," Every internal link becomes a redirect; set it explicitly and verify against a real deploy.",[43,1360,1361,1364,1365,1368],{},[1004,1362,1363],{},"Assuming image optimisation still works."," It needs a server; set ",[17,1366,1367],{},"unoptimized"," or configure a loader pointing at an image CDN.",[43,1370,1371,1374],{},[1004,1372,1373],{},"Client-only content."," Anything rendered in an effect is absent from the exported HTML, so it is invisible to search engines and lands after hydration.",[43,1376,1377,1380],{},[1004,1378,1379],{},"Enumerating from a live API without pinning."," A build whose route list comes from a changing API is not reproducible; snapshot the response.",[43,1382,1383,1386,1387,1389,1390,1392],{},[1004,1384,1385],{},"Rollback:"," removing ",[17,1388,19],{}," restores the server target, and dynamic routes keep working — ",[17,1391,26],{}," remains valid, it simply becomes an optimisation again rather than a definition.",[35,1394,1396],{"id":1395},"conclusion","Conclusion",[14,1398,1399,1400,1402,1403,1405,1406,33],{},"Static export asks one thing: know every route at build time. Answer that with ",[17,1401,26],{}," over your content directory, use catch-all segments for nested documentation, set ",[17,1404,512],{}," so gaps fail loudly, and settle the trailing-slash question once against a real deployment. What you lose is Next's server-side features, most of which your host's edge layer already offers; what you gain is a site that is files at the edge. The wider case for this target is in ",[29,1407,32],{"href":31},[35,1409,1411],{"id":1410},"faq","FAQ",[1413,1414,1416],"h3",{"id":1415},"what-happens-to-a-dynamic-route-that-generatestaticparams-does-not-return","What happens to a dynamic route that generateStaticParams does not return?",[14,1418,1419,1420,1422],{},"Nothing is exported for it, so the host serves a 404. There is no fallback rendering under ",[17,1421,19],{},", which means the params function is the complete list of pages your site has — anything missing simply does not exist.",[1413,1424,1426],{"id":1425},"can-i-use-catch-all-routes-with-static-export","Can I use catch-all routes with static export?",[14,1428,1429,1430,1432],{},"Yes, and they are the natural fit for documentation trees. A catch-all segment plus a ",[17,1431,26],{}," that walks your content directory produces one page per document, with the path segments coming from the file structure.",[1413,1434,1436],{"id":1435},"why-do-my-exported-urls-have-a-trailing-slash-sometimes-and-not-others","Why do my exported URLs have a trailing slash sometimes and not others?",[14,1438,1439,1440,1442,1443,1446,1447,1450],{},"Because the ",[17,1441,836],{}," config option decides whether the export writes ",[17,1444,1445],{},"about.html"," or ",[17,1448,1449],{},"about\u002Findex.html",", and the host then normalises differently. Set it explicitly and match it to your host's behaviour, or half your internal links become redirects.",[1413,1452,1454],{"id":1453},"does-middleware-work-with-output-export","Does middleware work with output export?",[14,1456,1457],{},"No. Middleware, route handlers, image optimisation and on-demand revalidation all require a server. Under export those features are unavailable, which is the main constraint to check before choosing this target.",[1413,1459,1461],{"id":1460},"how-do-i-handle-a-page-whose-data-changes-without-a-rebuild","How do I handle a page whose data changes without a rebuild?",[14,1463,1464],{},"You rebuild, or you fetch it on the client. Static export has no revalidation, so anything that must be current either triggers a deploy or is loaded at runtime from an API the page calls directly.",[35,1466,1468],{"id":1467},"related","Related",[40,1470,1471,1479,1486,1493,1500],{},[43,1472,1473,1229,1476,1478],{},[1004,1474,1475],{},"Parent:",[29,1477,32],{"href":31}," — when this target is the right choice.",[43,1480,1481,1485],{},[29,1482,1484],{"href":1483},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fmigrating-from-gatsby-to-nextjs-static-export\u002F","Migrating From Gatsby to Next.js Static Export"," — getting to this target from another React framework.",[43,1487,1488,1492],{},[29,1489,1491],{"href":1490},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fnextjs-static-export-vs-astro-for-marketing-sites\u002F","Next.js Static Export vs Astro for Marketing Sites"," — the alternative when the app constraint does not apply.",[43,1494,1495,1499],{},[29,1496,1498],{"href":1497},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fchoosing-an-ssg-for-api-reference-documentation\u002F","Choosing an SSG for API Reference Documentation"," — generated routes at much larger scale.",[43,1501,1502,1504],{},[29,1503,1336],{"href":1335}," — what export does not remove.",[1506,1507,1508],"style",{},"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 .sVt8B, html code.shiki .sVt8B{--shiki-default:#24292E;--shiki-dark:#E1E4E8}html pre.shiki code .sZZnC, html code.shiki .sZZnC{--shiki-default:#032F62;--shiki-dark:#9ECBFF}html pre.shiki code .sScJk, html code.shiki .sScJk{--shiki-default:#6F42C1;--shiki-dark:#B392F0}html pre.shiki code .sj4cs, html code.shiki .sj4cs{--shiki-default:#005CC5;--shiki-dark:#79B8FF}html pre.shiki code .s4XuR, html code.shiki .s4XuR{--shiki-default:#E36209;--shiki-dark:#FFAB70}html pre.shiki code .snhLl, html code.shiki .snhLl{--shiki-default:#22863A;--shiki-default-font-weight:bold;--shiki-dark:#85E89D;--shiki-dark-font-weight:bold}html pre.shiki code .sA_wV, html code.shiki .sA_wV{--shiki-default:#032F62;--shiki-dark:#DBEDFF}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);}",{"title":74,"searchDepth":88,"depth":88,"links":1510},[1511,1512,1513,1514,1515,1516,1517,1518,1519,1520,1527],{"id":37,"depth":88,"text":38},{"id":60,"depth":88,"text":61},{"id":647,"depth":88,"text":648},{"id":829,"depth":88,"text":830},{"id":994,"depth":88,"text":995},{"id":1219,"depth":88,"text":1220},{"id":1253,"depth":88,"text":1254},{"id":1339,"depth":88,"text":1340},{"id":1395,"depth":88,"text":1396},{"id":1410,"depth":88,"text":1411,"children":1521},[1522,1523,1524,1525,1526],{"id":1415,"depth":109,"text":1416},{"id":1425,"depth":109,"text":1426},{"id":1435,"depth":109,"text":1436},{"id":1453,"depth":109,"text":1454},{"id":1460,"depth":109,"text":1461},{"id":1467,"depth":88,"text":1468},[1529,1531,1534,1535],{"name":1530,"item":247},"Home",{"name":1532,"item":1533},"Choosing the Right Static Site Generator for Production","\u002Fchoosing-the-right-static-site-generator-for-production\u002F",{"name":32,"item":31},{"name":5,"item":1536},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export\u002F","2026-08-01","generateStaticParams, catch-all segments, trailing slashes and the 404 problem: how to make dynamic routes work under output export without a server.",[1540,1542,1544,1546,1547],{"q":1416,"a":1541},"Nothing is exported for it, so the host serves a 404. There is no fallback rendering under output export, which means the params function is the complete list of pages your site has — anything missing simply does not exist.",{"q":1426,"a":1543},"Yes, and they are the natural fit for documentation trees. A catch-all segment plus a generateStaticParams that walks your content directory produces one page per document, with the path segments coming from the file structure.",{"q":1436,"a":1545},"Because the trailingSlash config option decides whether the export writes about.html or about\u002Findex.html, and the host then normalises differently. Set it explicitly and match it to your host's behaviour, or half your internal links become redirects.",{"q":1454,"a":1457},{"q":1461,"a":1464},{},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export",{"title":5,"description":1538},"choosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites\u002Fhandling-dynamic-routes-in-nextjs-static-export\u002Findex","mCCnaEQm9fsmKAWPAk14hbukGFvEF9EzuALW4UIQbW0",[1554,1557,1560,1563,1566,1569,1572,1575,1577,1580,1583,1586,1589,1592,1595,1598,1601,1604,1605,1607,1610,1613,1616,1618,1621,1624,1627,1630,1633,1636,1639,1642,1645,1648,1651,1654,1657,1660,1663,1666,1669,1672,1675,1678,1681,1684,1686,1689,1692,1695,1698,1701,1704,1707,1710,1713,1716,1719,1722,1725,1728,1730,1733,1736,1739,1742,1745,1748,1751,1754,1757,1760,1763,1766,1769,1772,1775,1778,1781,1784,1787,1790,1793],{"path":1555,"title":1556},"\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":1558,"title":1559},"\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":1561,"title":1562},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fastro-vs-eleventy-for-documentation-sites","Astro vs Eleventy for Documentation Sites",{"path":1564,"title":1565},"\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":1567,"title":1568},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fhugo-build-times-for-large-repositories","Hugo Build Times for Large Repositories",{"path":1570,"title":1571},"\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":1573,"title":1574},"\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":1576,"title":1532},"\u002Fchoosing-the-right-static-site-generator-for-production",{"path":1578,"title":1579},"\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":1581,"title":1582},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fjekyll-plugin-ecosystem","Jekyll Plugin Ecosystem",{"path":1584,"title":1585},"\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":1587,"title":1588},"\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":1590,"title":1591},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fmigrating-between-static-site-generators","Migrating Between Static Site Generators",{"path":1593,"title":1594},"\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":1596,"title":1597},"\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":1599,"title":1600},"\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":1602,"title":1603},"\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":1549,"title":5},{"path":1606,"title":32},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fnextjs-static-export-for-content-sites",{"path":1608,"title":1609},"\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":1611,"title":1612},"\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":1614,"title":1615},"\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":1617,"title":1498},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix\u002Fchoosing-an-ssg-for-api-reference-documentation",{"path":1619,"title":1620},"\u002Fchoosing-the-right-static-site-generator-for-production\u002Fssg-framework-selection-matrix","SSG Framework Selection Matrix",{"path":1622,"title":1623},"\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":1625,"title":1626},"\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":1628,"title":1629},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcdn-caching-rules-for-ssgs","CDN Caching Rules for SSGs",{"path":1631,"title":1632},"\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":1634,"title":1635},"\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":1637,"title":1638},"\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":1640,"title":1641},"\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":1643,"title":1644},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Ffixing-cls-from-late-loading-embeds","Fixing CLS From Late-Loading Embeds",{"path":1646,"title":1647},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites","Cumulative Layout Shift Fixes for Static Sites",{"path":1649,"title":1650},"\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":1652,"title":1653},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fcumulative-layout-shift-fixes-for-static-sites\u002Freserving-space-for-images-and-embeds-to-stop-layout-shift","Reserving Space for Images and Embeds",{"path":1655,"title":1656},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Ffont-loading-strategies-for-static-sites","Font Loading Strategies for Static Sites",{"path":1658,"title":1659},"\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":1661,"title":1662},"\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":1664,"title":1665},"\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":1667,"title":1668},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fimage-optimization-pipelines-in-astro","Image Optimization Pipelines in Astro",{"path":1670,"title":1671},"\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":1673,"title":1674},"\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":1676,"title":1677},"\u002Fperformance-optimization-core-web-vitals-for-ssgs","Core Web Vitals Optimization for SSGs",{"path":1679,"title":1680},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering\u002Fastro-islands-vs-full-hydration-performance","Astro Islands vs Full Hydration Performance",{"path":1682,"title":1683},"\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":1685,"title":1336},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fjavascript-hydration-partial-rendering",{"path":1687,"title":1688},"\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":1690,"title":1691},"\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":1693,"title":1694},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Flargest-contentful-paint-optimization-for-static-sites","Largest Contentful Paint Optimization for Static Sites",{"path":1696,"title":1697},"\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":1699,"title":1700},"\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":1702,"title":1703},"\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":1705,"title":1706},"\u002Fperformance-optimization-core-web-vitals-for-ssgs\u002Fthird-party-script-performance-on-static-sites","Third-Party Script Performance on Static Sites",{"path":1708,"title":1709},"\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":1711,"title":1712},"\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":1714,"title":1715},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup\u002Fautomating-eleventy-deployments-with-cloudflare-pages","Automating Eleventy Deployments on Cloudflare Pages",{"path":1717,"title":1718},"\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":1720,"title":1721},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcloudflare-pages-edge-caching-setup","Cloudflare Pages Edge Caching Setup",{"path":1723,"title":1724},"\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":1726,"title":1727},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams","Content Workflows for Documentation Teams",{"path":1729,"title":1249},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fcontent-workflows-for-documentation-teams\u002Fscheduling-content-publication-with-cron-triggered-builds",{"path":1731,"title":1732},"\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":1734,"title":1735},"\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":1737,"title":1738},"\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":1740,"title":1741},"\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":1743,"title":1744},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fgithub-actions-for-automated-ssg-builds","GitHub Actions for Automated SSG Builds",{"path":1746,"title":1747},"\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":1749,"title":1750},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs\u002Fenabling-incremental-builds-in-eleventy","Enabling Incremental Builds in Eleventy",{"path":1752,"title":1753},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fincremental-builds-and-build-caching-for-ssgs","Incremental Builds and Build Caching for SSGs",{"path":1755,"title":1756},"\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":1758,"title":1759},"\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":1761,"title":1762},"\u002Fproduction-ready-deployment-cicd-workflows","Production-Ready Deployment & CI\u002FCD for SSGs",{"path":1764,"title":1765},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies","Netlify vs Vercel Deployment Strategies",{"path":1767,"title":1768},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fnetlify-vs-vercel-deployment-strategies\u002Fnetlify-build-hooks-for-content-updates","Netlify Build Hooks for Content Updates",{"path":1770,"title":1771},"\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":1773,"title":1774},"\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":1776,"title":1777},"\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":1779,"title":1780},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests\u002Fcleaning-up-stale-preview-deployments","Cleaning Up Stale Preview Deployments",{"path":1782,"title":1783},"\u002Fproduction-ready-deployment-cicd-workflows\u002Fpreview-environments-for-pull-requests","Preview Environments for Pull Requests",{"path":1785,"title":1786},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites\u002Fatomic-deploys-vs-incremental-uploads","Atomic Deploys vs Incremental Uploads",{"path":1788,"title":1789},"\u002Fproduction-ready-deployment-cicd-workflows\u002Frollbacks-and-deploy-safety-for-static-sites","Rollbacks and Deploy Safety for Static Sites",{"path":1791,"title":1792},"\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":1794,"title":1795},"\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",1785611671155]