[{"data":1,"prerenderedAt":1524},["ShallowReactive",2],{"content:\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fenforcing-bundle-budgets-with-size-limit":3,"surroundings:\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fenforcing-bundle-budgets-with-size-limit":1516},{"id":4,"title":5,"body":6,"description":1495,"extension":1496,"meta":1497,"navigation":1509,"path":1510,"seo":1511,"stem":1514,"__hash__":1515},"content\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fenforcing-bundle-budgets-with-size-limit\u002Findex.md","Enforcing Bundle Budgets with size-limit",{"type":7,"value":8,"toc":1480},"minimark",[9,14,34,37,182,187,216,220,226,232,238,244,359,363,368,412,596,603,607,614,904,1014,1017,1021,1188,1191,1315,1319,1322,1326,1329,1333,1348,1352,1393,1397,1420,1429,1438,1442,1465,1470,1473,1476],[10,11,13],"h1",{"id":12},"how-to-enforce-javascript-bundle-budgets-with-size-limit","How to Enforce JavaScript Bundle Budgets with size-limit",[15,16,17,18,23,24,28,29,33],"p",{},"This guide implements the size half of ",[19,20,22],"a",{"href":21},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002F","JavaScript Performance Budgets",", within ",[19,25,27],{"href":26},"\u002Fjavascript-bundle-optimization-code-splitting\u002F","JavaScript Bundle Optimization & Code Splitting",". ",[30,31,32],"code",{},"size-limit"," is a small CLI that measures files or import paths after bundling and compression and fails with a non-zero exit code when any exceeds its limit. It is deterministic, fast enough to run on every commit, and its output is readable by people who have never opened a bundle analyser.",[15,35,36],{},"The tool is simple; using it well is not. Budgets must cover the files a route actually loads (not whatever a glob happens to match), measure the size that matters for each concern, and be set at levels that hold the line without failing every other build. This guide covers configuration for app bundles and libraries, the measurement options, and the CI wiring that makes failures actionable.",[15,38,39],{},[40,41,47,48,47,55,47,59,47,62,47,80,47,88,47,94,47,102,47,109,47,114,47,117,47,121,47,124,47,130,47,134,47,137,47,141,47,144,47,148,47,151,47,154,47,158,47,161,47,170,47,174,47,178,47],"svg",{"viewBox":42,"width":43,"role":44,"ariaLabel":45,"style":46},"0 0 760 149","100%","img","Steps from building the app in CI to measuring each budget, failing on overage and reporting the diff to the author.","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)"," ",[49,50],"rect",{"className":51,"x":53,"y":53,"width":43,"height":43,"fill":54},[52],"svg-canvas","0","#ffffff",[56,57,58],"title",{},"size-limit in a pull request pipeline",[60,61,45],"desc",{},[63,64,65],"defs",{},[66,67,74],"marker",{"id":68,"viewBox":69,"refX":70,"refY":71,"markerWidth":72,"markerHeight":72,"orient":73},"facfc4384e","0 0 10 10","9","5","7","auto-start-reverse",[75,76],"path",{"d":77,"fill":78,"style":79},"M0 0 L10 5 L0 10 z","currentColor","fill-opacity:0.7",[49,81],{"x":82,"y":82,"width":83,"height":84,"rx":85,"fill":86,"stroke":78,"style":87},"1","758","147","10","none","stroke-opacity:0.18",[89,90,58],"text",{"x":91,"y":92,"fill":78,"style":93},"28.0","34.0","font-size:16px;font-weight:700",[49,95],{"x":91,"y":96,"width":97,"height":98,"rx":99,"fill":100,"stroke":100,"style":101},"56.0","108.8","71.0","6","#0466c8","fill-opacity:0.14;stroke-opacity:0.9",[89,103,108],{"x":104,"y":105,"fill":78,"style":106,"textAnchor":107},"82.4","78.0","font-size:13px;font-weight:700","middle","Build",[89,110,113],{"x":104,"y":111,"fill":78,"style":112,"textAnchor":107},"96.0","font-size:12px","production output",[49,115],{"x":116,"y":96,"width":97,"height":98,"rx":99,"fill":100,"stroke":100,"style":101},"176.8",[89,118,120],{"x":119,"y":105,"fill":78,"style":106,"textAnchor":107},"231.2","Measure",[89,122,123],{"x":119,"y":111,"fill":78,"style":112,"textAnchor":107},"per budget entry",[49,125],{"x":126,"y":96,"width":97,"height":98,"rx":99,"fill":127,"stroke":128,"style":129},"325.6","#ffc300","#b8860b","fill-opacity:0.24;stroke-opacity:0.9",[89,131,133],{"x":132,"y":105,"fill":78,"style":106,"textAnchor":107},"380.0","Compare",[89,135,136],{"x":132,"y":111,"fill":78,"style":112,"textAnchor":107},"limit + base",[89,138,140],{"x":132,"y":139,"fill":78,"style":112,"textAnchor":107},"112.0","branch",[49,142],{"x":143,"y":96,"width":97,"height":98,"rx":99,"fill":100,"stroke":100,"style":101},"474.4",[89,145,147],{"x":146,"y":105,"fill":78,"style":106,"textAnchor":107},"528.8","Fail or pass",[89,149,150],{"x":146,"y":111,"fill":78,"style":112,"textAnchor":107},"exit code",[49,152],{"x":153,"y":96,"width":97,"height":98,"rx":99,"fill":100,"stroke":100,"style":101},"623.2",[89,155,157],{"x":156,"y":105,"fill":78,"style":106,"textAnchor":107},"677.6","Report",[89,159,160],{"x":156,"y":111,"fill":78,"style":112,"textAnchor":107},"PR comment",[162,163],"line",{"x1":164,"y1":165,"x2":166,"y2":165,"stroke":78,"strokeWidth":167,"style":168,"markerEnd":169},"136.8","91.5","174.8","1.5","stroke-opacity:0.6","url(#facfc4384e)",[162,171],{"x1":172,"y1":165,"x2":173,"y2":165,"stroke":78,"strokeWidth":167,"style":168,"markerEnd":169},"285.6","323.6",[162,175],{"x1":176,"y1":165,"x2":177,"y2":165,"stroke":78,"strokeWidth":167,"style":168,"markerEnd":169},"434.4","472.4",[162,179],{"x1":180,"y1":165,"x2":181,"y2":165,"stroke":78,"strokeWidth":167,"style":168,"markerEnd":169},"583.2","621.2",[183,184,186],"h2",{"id":185},"rapid-diagnosis","Rapid Diagnosis",[188,189,190,198,204,210],"ul",{},[191,192,193,197],"li",{},[194,195,196],"strong",{},"Do you have any byte budget today?"," If regressions are noticed only in Lighthouse or RUM, start here.",[191,199,200,203],{},[194,201,202],{},"Do budgets cover routes or files?"," File-level budgets break when chunk names change and miss weight that moves between files.",[191,205,206,209],{},[194,207,208],{},"Which size do they measure?"," Gzip-only budgets under-predict parse cost; uncompressed-only budgets ignore transfer.",[191,211,212,215],{},[194,213,214],{},"Do failures explain themselves?"," A red check with no diff gets rerun or overridden; a diff showing \"+42KB: date-fns locale added\" gets fixed.",[183,217,219],{"id":218},"root-cause-analysis-why-budgets-get-ignored","Root Cause Analysis: Why Budgets Get Ignored",[15,221,222,225],{},[194,223,224],{},"1. Wrong coverage."," Globs that match too much (all chunks) produce a total nobody can act on; globs that match too little miss the real initial load.",[15,227,228,231],{},[194,229,230],{},"2. Unrealistic limits."," Budgets set at aspirational targets fail constantly and get disabled.",[15,233,234,237],{},[194,235,236],{},"3. Noisy or slow checks."," Running timing-based checks on every commit introduces flakiness; size checks must stay deterministic.",[15,239,240,243],{},[194,241,242],{},"4. No ownership."," Nobody is responsible for deciding whether a failing budget should be raised or the change reworked.",[15,245,246],{},[40,247,47,250,47,253,47,256,47,258,47,261,47,263,47,268,47,275,47,279,47,283,47,286,47,290,47,294,47,298,47,300,47,303,47,305,47,308,47,311,47,315,47,317,47,320,47,322,47,325,47,328,47,332,47,334,47,337,47,339,47,342,47,345,47,349,47,351,47,354,47,356,47],{"viewBox":248,"width":43,"role":44,"ariaLabel":249,"style":46},"0 0 760 228","The measurement modes size-limit supports and what each is good for.",[49,251],{"className":252,"x":53,"y":53,"width":43,"height":43,"fill":54},[52],[56,254,255],{},"size-limit measurement options",[60,257,249],{},[49,259],{"x":82,"y":82,"width":83,"height":260,"rx":85,"fill":86,"stroke":78,"style":87},"226",[89,262,255],{"x":91,"y":92,"fill":78,"style":93},[49,264],{"x":91,"y":96,"width":265,"height":266,"rx":53,"fill":78,"stroke":78,"style":267},"197.6","30.0","fill-opacity:0.06;stroke-opacity:0.4",[89,269,274],{"x":270,"y":271,"fill":78,"style":272,"textAnchor":273},"38.0","75.5","font-size:12.5px;font-weight:700","start","Option",[49,276],{"x":277,"y":96,"width":278,"height":266,"rx":53,"fill":78,"stroke":78,"style":267},"225.6","253.2",[89,280,282],{"x":281,"y":271,"fill":78,"style":272,"textAnchor":107},"352.2","Measures",[49,284],{"x":285,"y":96,"width":278,"height":266,"rx":53,"fill":78,"stroke":78,"style":267},"478.8",[89,287,289],{"x":288,"y":271,"fill":78,"style":272,"textAnchor":107},"605.4","Use for",[49,291],{"x":91,"y":292,"width":265,"height":266,"rx":53,"fill":86,"stroke":78,"style":293},"86.0","stroke-opacity:0.35",[89,295,297],{"x":270,"y":296,"fill":78,"style":272,"textAnchor":273},"105.5","gzip: false, brotli: false",[49,299],{"x":277,"y":292,"width":278,"height":266,"rx":53,"fill":100,"stroke":100,"style":101},[89,301,302],{"x":281,"y":296,"fill":78,"style":112,"textAnchor":107},"uncompressed bytes",[49,304],{"x":285,"y":292,"width":278,"height":266,"rx":53,"fill":86,"stroke":78,"style":293},[89,306,307],{"x":288,"y":296,"fill":78,"style":112,"textAnchor":107},"Parse and compile cost",[49,309],{"x":91,"y":310,"width":265,"height":266,"rx":53,"fill":86,"stroke":78,"style":293},"116.0",[89,312,314],{"x":270,"y":313,"fill":78,"style":272,"textAnchor":273},"135.5","brotli: true",[49,316],{"x":277,"y":310,"width":278,"height":266,"rx":53,"fill":100,"stroke":100,"style":101},[89,318,319],{"x":281,"y":313,"fill":78,"style":112,"textAnchor":107},"brotli bytes",[49,321],{"x":285,"y":310,"width":278,"height":266,"rx":53,"fill":86,"stroke":78,"style":293},[89,323,324],{"x":288,"y":313,"fill":78,"style":112,"textAnchor":107},"Transfer on modern CDNs",[49,326],{"x":91,"y":327,"width":265,"height":266,"rx":53,"fill":86,"stroke":78,"style":293},"146.0",[89,329,331],{"x":270,"y":330,"fill":78,"style":272,"textAnchor":273},"165.5","import: '{ X }'",[49,333],{"x":277,"y":327,"width":278,"height":266,"rx":53,"fill":100,"stroke":100,"style":101},[89,335,336],{"x":281,"y":330,"fill":78,"style":112,"textAnchor":107},"cost of importing one export",[49,338],{"x":285,"y":327,"width":278,"height":266,"rx":53,"fill":86,"stroke":78,"style":293},[89,340,341],{"x":288,"y":330,"fill":78,"style":112,"textAnchor":107},"Library consumers",[49,343],{"x":91,"y":344,"width":265,"height":266,"rx":53,"fill":86,"stroke":78,"style":293},"176.0",[89,346,348],{"x":270,"y":347,"fill":78,"style":272,"textAnchor":273},"195.5","running: true (@size-limit\u002Ftime)",[49,350],{"x":277,"y":344,"width":278,"height":266,"rx":53,"fill":78,"stroke":78,"style":267},[89,352,353],{"x":281,"y":347,"fill":78,"style":112,"textAnchor":107},"estimated execution time",[49,355],{"x":285,"y":344,"width":278,"height":266,"rx":53,"fill":86,"stroke":78,"style":293},[89,357,358],{"x":288,"y":347,"fill":78,"style":112,"textAnchor":107},"Rough CPU signal; noisy",[183,360,362],{"id":361},"step-by-step-resolution","Step-by-Step Resolution",[364,365,367],"h3",{"id":366},"_1-install-and-define-budgets-per-route","1. Install and define budgets per route",[369,370,375],"pre",{"className":371,"code":372,"language":373,"meta":374,"style":374},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","npm install --save-dev size-limit @size-limit\u002Ffile\n# For libraries measuring import cost through a bundler:\n# npm install --save-dev @size-limit\u002Fesbuild\n","bash","",[30,376,377,399,406],{"__ignoreMap":374},[378,379,381,385,389,393,396],"span",{"class":162,"line":380},1,[378,382,384],{"class":383},"sQw3B","npm",[378,386,388],{"class":387},"sZ8jY"," install",[378,390,392],{"class":391},"sPXB4"," --save-dev",[378,394,395],{"class":387}," size-limit",[378,397,398],{"class":387}," @size-limit\u002Ffile\n",[378,400,402],{"class":162,"line":401},2,[378,403,405],{"class":404},"sjfSM","# For libraries measuring import cost through a bundler:\n",[378,407,409],{"class":162,"line":408},3,[378,410,411],{"class":404},"# npm install --save-dev @size-limit\u002Fesbuild\n",[369,413,417],{"className":414,"code":415,"language":416,"meta":374,"style":374},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F .size-limit.cjs\nmodule.exports = [\n  { name: 'Landing — initial JS (raw)',    path: ['dist\u002Fassets\u002Findex-*.js', 'dist\u002Fassets\u002Fframework-*.js'], limit: '320 KB', gzip: false, brotli: false },\n  { name: 'Landing — initial JS (brotli)', path: ['dist\u002Fassets\u002Findex-*.js', 'dist\u002Fassets\u002Fframework-*.js'], limit: '98 KB',  brotli: true },\n  { name: 'Product route chunk (raw)',     path: 'dist\u002Fassets\u002FProductPage-*.js', limit: '90 KB', gzip: false, brotli: false },\n  { name: 'Checkout route chunk (raw)',    path: 'dist\u002Fassets\u002FCheckoutPage-*.js', limit: '70 KB', gzip: false, brotli: false },\n];\n\u002F\u002F trade-off: two budgets per route (raw + brotli) double the maintenance but\n\u002F\u002F catch both kinds of regression: a minified-but-huge module (raw) and an\n\u002F\u002F incompressible asset like inlined images (brotli).\n","javascript",[30,418,419,424,443,483,513,543,572,578,584,590],{"__ignoreMap":374},[378,420,421],{"class":162,"line":380},[378,422,423],{"class":404},"\u002F\u002F .size-limit.cjs\n",[378,425,426,429,433,436,440],{"class":162,"line":401},[378,427,428],{"class":391},"module",[378,430,432],{"class":431},"saISM",".",[378,434,435],{"class":391},"exports",[378,437,439],{"class":438},"sPARh"," =",[378,441,442],{"class":431}," [\n",[378,444,445,448,451,454,457,460,463,466,469,472,475,478,480],{"class":162,"line":408},[378,446,447],{"class":431},"  { name: ",[378,449,450],{"class":387},"'Landing — initial JS (raw)'",[378,452,453],{"class":431},",    path: [",[378,455,456],{"class":387},"'dist\u002Fassets\u002Findex-*.js'",[378,458,459],{"class":431},", ",[378,461,462],{"class":387},"'dist\u002Fassets\u002Fframework-*.js'",[378,464,465],{"class":431},"], limit: ",[378,467,468],{"class":387},"'320 KB'",[378,470,471],{"class":431},", gzip: ",[378,473,474],{"class":391},"false",[378,476,477],{"class":431},", brotli: ",[378,479,474],{"class":391},[378,481,482],{"class":431}," },\n",[378,484,486,488,491,494,496,498,500,502,505,508,511],{"class":162,"line":485},4,[378,487,447],{"class":431},[378,489,490],{"class":387},"'Landing — initial JS (brotli)'",[378,492,493],{"class":431},", path: [",[378,495,456],{"class":387},[378,497,459],{"class":431},[378,499,462],{"class":387},[378,501,465],{"class":431},[378,503,504],{"class":387},"'98 KB'",[378,506,507],{"class":431},",  brotli: ",[378,509,510],{"class":391},"true",[378,512,482],{"class":431},[378,514,516,518,521,524,527,530,533,535,537,539,541],{"class":162,"line":515},5,[378,517,447],{"class":431},[378,519,520],{"class":387},"'Product route chunk (raw)'",[378,522,523],{"class":431},",     path: ",[378,525,526],{"class":387},"'dist\u002Fassets\u002FProductPage-*.js'",[378,528,529],{"class":431},", limit: ",[378,531,532],{"class":387},"'90 KB'",[378,534,471],{"class":431},[378,536,474],{"class":391},[378,538,477],{"class":431},[378,540,474],{"class":391},[378,542,482],{"class":431},[378,544,546,548,551,554,557,559,562,564,566,568,570],{"class":162,"line":545},6,[378,547,447],{"class":431},[378,549,550],{"class":387},"'Checkout route chunk (raw)'",[378,552,553],{"class":431},",    path: ",[378,555,556],{"class":387},"'dist\u002Fassets\u002FCheckoutPage-*.js'",[378,558,529],{"class":431},[378,560,561],{"class":387},"'70 KB'",[378,563,471],{"class":431},[378,565,474],{"class":391},[378,567,477],{"class":431},[378,569,474],{"class":391},[378,571,482],{"class":431},[378,573,575],{"class":162,"line":574},7,[378,576,577],{"class":431},"];\n",[378,579,581],{"class":162,"line":580},8,[378,582,583],{"class":404},"\u002F\u002F trade-off: two budgets per route (raw + brotli) double the maintenance but\n",[378,585,587],{"class":162,"line":586},9,[378,588,589],{"class":404},"\u002F\u002F catch both kinds of regression: a minified-but-huge module (raw) and an\n",[378,591,593],{"class":162,"line":592},10,[378,594,595],{"class":404},"\u002F\u002F incompressible asset like inlined images (brotli).\n",[15,597,598,599,602],{},"Expected outcome: ",[30,600,601],{},"npx size-limit"," prints each budget with its current size and fails if any is over.",[364,604,606],{"id":605},"_2-generate-route-file-lists-from-the-manifest","2. Generate route file lists from the manifest",[15,608,609,610,613],{},"Globs are brittle; a small script can produce the exact file list for each entry from the build manifest and write it to a JSON file that ",[30,611,612],{},".size-limit.cjs"," reads.",[369,615,617],{"className":414,"code":616,"language":416,"meta":374,"style":374},"\u002F\u002F scripts\u002Froute-files.mjs — writes dist\u002Froute-files.json { \"src\u002Fmain.ts\": [\"assets\u002Findex-a1.js\", ...] }\nimport { writeFileSync } from 'node:fs';\nimport manifest from '..\u002Fdist\u002F.vite\u002Fmanifest.json' with { type: 'json' };\nconst closure = (k, s = new Set()) => (s.has(k) ? s : (s.add(k), (manifest[k].imports ?? []).forEach((d) => closure(d, s)), s));\nconst out = {};\nfor (const [k, v] of Object.entries(manifest)) if (v.isEntry || v.isDynamicEntry) out[k] = [...closure(k)].map((x) => `dist\u002F${manifest[x].file}`);\nwriteFileSync('dist\u002Froute-files.json', JSON.stringify(out, null, 2));\n\u002F\u002F trade-off: this follows static imports only. Code that calls import() during\n\u002F\u002F startup still loads extra chunks; add those to the budget explicitly.\n",[30,618,619,624,641,665,752,764,859,894,899],{"__ignoreMap":374},[378,620,621],{"class":162,"line":380},[378,622,623],{"class":404},"\u002F\u002F scripts\u002Froute-files.mjs — writes dist\u002Froute-files.json { \"src\u002Fmain.ts\": [\"assets\u002Findex-a1.js\", ...] }\n",[378,625,626,629,632,635,638],{"class":162,"line":401},[378,627,628],{"class":438},"import",[378,630,631],{"class":431}," { writeFileSync } ",[378,633,634],{"class":438},"from",[378,636,637],{"class":387}," 'node:fs'",[378,639,640],{"class":431},";\n",[378,642,643,645,648,650,653,656,659,662],{"class":162,"line":408},[378,644,628],{"class":438},[378,646,647],{"class":431}," manifest ",[378,649,634],{"class":438},[378,651,652],{"class":387}," '..\u002Fdist\u002F.vite\u002Fmanifest.json'",[378,654,655],{"class":438}," with",[378,657,658],{"class":431}," { type: ",[378,660,661],{"class":387},"'json'",[378,663,664],{"class":431}," };\n",[378,666,667,670,674,676,679,682,684,687,689,692,695,698,701,704,707,710,713,716,719,721,724,727,730,733,736,739,742,745,747,749],{"class":162,"line":485},[378,668,669],{"class":438},"const",[378,671,673],{"class":672},"smZ65"," closure",[378,675,439],{"class":438},[378,677,678],{"class":431}," (",[378,680,681],{"class":383},"k",[378,683,459],{"class":431},[378,685,686],{"class":383},"s",[378,688,439],{"class":438},[378,690,691],{"class":438}," new",[378,693,694],{"class":672}," Set",[378,696,697],{"class":431},"()) ",[378,699,700],{"class":438},"=>",[378,702,703],{"class":431}," (s.",[378,705,706],{"class":672},"has",[378,708,709],{"class":431},"(k) ",[378,711,712],{"class":438},"?",[378,714,715],{"class":431}," s ",[378,717,718],{"class":438},":",[378,720,703],{"class":431},[378,722,723],{"class":672},"add",[378,725,726],{"class":431},"(k), (manifest[k].imports ",[378,728,729],{"class":438},"??",[378,731,732],{"class":431}," []).",[378,734,735],{"class":672},"forEach",[378,737,738],{"class":431},"((",[378,740,741],{"class":383},"d",[378,743,744],{"class":431},") ",[378,746,700],{"class":438},[378,748,673],{"class":672},[378,750,751],{"class":431},"(d, s)), s));\n",[378,753,754,756,759,761],{"class":162,"line":515},[378,755,669],{"class":438},[378,757,758],{"class":391}," out",[378,760,439],{"class":438},[378,762,763],{"class":431}," {};\n",[378,765,766,769,771,773,776,778,780,783,786,789,792,795,798,801,804,807,810,813,815,818,821,824,827,829,832,834,836,839,842,845,847,850,853,856],{"class":162,"line":545},[378,767,768],{"class":438},"for",[378,770,678],{"class":431},[378,772,669],{"class":438},[378,774,775],{"class":431}," [",[378,777,681],{"class":391},[378,779,459],{"class":431},[378,781,782],{"class":391},"v",[378,784,785],{"class":431},"] ",[378,787,788],{"class":438},"of",[378,790,791],{"class":431}," Object.",[378,793,794],{"class":672},"entries",[378,796,797],{"class":431},"(manifest)) ",[378,799,800],{"class":438},"if",[378,802,803],{"class":431}," (v.isEntry ",[378,805,806],{"class":438},"||",[378,808,809],{"class":431}," v.isDynamicEntry) out[k] ",[378,811,812],{"class":438},"=",[378,814,775],{"class":431},[378,816,817],{"class":438},"...",[378,819,820],{"class":672},"closure",[378,822,823],{"class":431},"(k)].",[378,825,826],{"class":672},"map",[378,828,738],{"class":431},[378,830,831],{"class":383},"x",[378,833,744],{"class":431},[378,835,700],{"class":438},[378,837,838],{"class":387}," `dist\u002F${",[378,840,841],{"class":431},"manifest",[378,843,844],{"class":387},"[",[378,846,831],{"class":431},[378,848,849],{"class":387},"].",[378,851,852],{"class":431},"file",[378,854,855],{"class":387},"}`",[378,857,858],{"class":431},");\n",[378,860,861,864,867,870,872,875,877,880,883,886,888,891],{"class":162,"line":574},[378,862,863],{"class":672},"writeFileSync",[378,865,866],{"class":431},"(",[378,868,869],{"class":387},"'dist\u002Froute-files.json'",[378,871,459],{"class":431},[378,873,874],{"class":391},"JSON",[378,876,432],{"class":431},[378,878,879],{"class":672},"stringify",[378,881,882],{"class":431},"(out, ",[378,884,885],{"class":391},"null",[378,887,459],{"class":431},[378,889,890],{"class":391},"2",[378,892,893],{"class":431},"));\n",[378,895,896],{"class":162,"line":580},[378,897,898],{"class":404},"\u002F\u002F trade-off: this follows static imports only. Code that calls import() during\n",[378,900,901],{"class":162,"line":586},[378,902,903],{"class":404},"\u002F\u002F startup still loads extra chunks; add those to the budget explicitly.\n",[369,905,907],{"className":414,"code":906,"language":416,"meta":374,"style":374},"\u002F\u002F .size-limit.cjs (manifest-driven)\nconst routes = require('.\u002Fdist\u002Froute-files.json');\nmodule.exports = [\n  { name: 'Landing initial', path: routes['src\u002Fmain.ts'], limit: '320 KB', gzip: false, brotli: false },\n  { name: 'Checkout route',  path: routes['src\u002Fpages\u002FCheckout.vue'], limit: '140 KB', gzip: false, brotli: false },\n];\n\u002F\u002F trade-off: requiring a generated file means size-limit must run after the\n\u002F\u002F build script. Wire both into one npm script so they cannot run out of order.\n",[30,908,909,914,933,945,972,1000,1004,1009],{"__ignoreMap":374},[378,910,911],{"class":162,"line":380},[378,912,913],{"class":404},"\u002F\u002F .size-limit.cjs (manifest-driven)\n",[378,915,916,918,921,923,926,928,931],{"class":162,"line":401},[378,917,669],{"class":438},[378,919,920],{"class":391}," routes",[378,922,439],{"class":438},[378,924,925],{"class":672}," require",[378,927,866],{"class":431},[378,929,930],{"class":387},"'.\u002Fdist\u002Froute-files.json'",[378,932,858],{"class":431},[378,934,935,937,939,941,943],{"class":162,"line":408},[378,936,428],{"class":391},[378,938,432],{"class":431},[378,940,435],{"class":391},[378,942,439],{"class":438},[378,944,442],{"class":431},[378,946,947,949,952,955,958,960,962,964,966,968,970],{"class":162,"line":485},[378,948,447],{"class":431},[378,950,951],{"class":387},"'Landing initial'",[378,953,954],{"class":431},", path: routes[",[378,956,957],{"class":387},"'src\u002Fmain.ts'",[378,959,465],{"class":431},[378,961,468],{"class":387},[378,963,471],{"class":431},[378,965,474],{"class":391},[378,967,477],{"class":431},[378,969,474],{"class":391},[378,971,482],{"class":431},[378,973,974,976,979,982,985,987,990,992,994,996,998],{"class":162,"line":515},[378,975,447],{"class":431},[378,977,978],{"class":387},"'Checkout route'",[378,980,981],{"class":431},",  path: routes[",[378,983,984],{"class":387},"'src\u002Fpages\u002FCheckout.vue'",[378,986,465],{"class":431},[378,988,989],{"class":387},"'140 KB'",[378,991,471],{"class":431},[378,993,474],{"class":391},[378,995,477],{"class":431},[378,997,474],{"class":391},[378,999,482],{"class":431},[378,1001,1002],{"class":162,"line":545},[378,1003,577],{"class":431},[378,1005,1006],{"class":162,"line":574},[378,1007,1008],{"class":404},"\u002F\u002F trade-off: requiring a generated file means size-limit must run after the\n",[378,1010,1011],{"class":162,"line":580},[378,1012,1013],{"class":404},"\u002F\u002F build script. Wire both into one npm script so they cannot run out of order.\n",[15,1015,1016],{},"Expected outcome: budgets follow the real import graph through chunking changes.",[364,1018,1020],{"id":1019},"_3-run-in-ci-and-fail-on-overage","3. Run in CI and fail on overage",[369,1022,1026],{"className":1023,"code":1024,"language":1025,"meta":374,"style":374},"language-yaml shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","# .github\u002Fworkflows\u002Fsize.yml\nname: size\non: pull_request\njobs:\n  size:\n    runs-on: ubuntu-latest\n    steps:\n      - uses: actions\u002Fcheckout@v4\n      - uses: actions\u002Fsetup-node@v4\n        with: { node-version: 22, cache: npm }\n      - run: npm ci\n      - run: npm run build && node scripts\u002Froute-files.mjs\n      - run: npx size-limit\n# trade-off: this fails the check but does not explain the change. Add the\n# per-PR comment from the next guide so authors see the diff without digging.\n","yaml",[30,1027,1028,1033,1045,1055,1063,1070,1080,1087,1100,1111,1139,1152,1164,1176,1182],{"__ignoreMap":374},[378,1029,1030],{"class":162,"line":380},[378,1031,1032],{"class":404},"# .github\u002Fworkflows\u002Fsize.yml\n",[378,1034,1035,1039,1042],{"class":162,"line":401},[378,1036,1038],{"class":1037},"sZBmE","name",[378,1040,1041],{"class":431},": ",[378,1043,1044],{"class":387},"size\n",[378,1046,1047,1050,1052],{"class":162,"line":408},[378,1048,1049],{"class":391},"on",[378,1051,1041],{"class":431},[378,1053,1054],{"class":387},"pull_request\n",[378,1056,1057,1060],{"class":162,"line":485},[378,1058,1059],{"class":1037},"jobs",[378,1061,1062],{"class":431},":\n",[378,1064,1065,1068],{"class":162,"line":515},[378,1066,1067],{"class":1037},"  size",[378,1069,1062],{"class":431},[378,1071,1072,1075,1077],{"class":162,"line":545},[378,1073,1074],{"class":1037},"    runs-on",[378,1076,1041],{"class":431},[378,1078,1079],{"class":387},"ubuntu-latest\n",[378,1081,1082,1085],{"class":162,"line":574},[378,1083,1084],{"class":1037},"    steps",[378,1086,1062],{"class":431},[378,1088,1089,1092,1095,1097],{"class":162,"line":580},[378,1090,1091],{"class":431},"      - ",[378,1093,1094],{"class":1037},"uses",[378,1096,1041],{"class":431},[378,1098,1099],{"class":387},"actions\u002Fcheckout@v4\n",[378,1101,1102,1104,1106,1108],{"class":162,"line":586},[378,1103,1091],{"class":431},[378,1105,1094],{"class":1037},[378,1107,1041],{"class":431},[378,1109,1110],{"class":387},"actions\u002Fsetup-node@v4\n",[378,1112,1113,1116,1119,1122,1124,1127,1129,1132,1134,1136],{"class":162,"line":592},[378,1114,1115],{"class":1037},"        with",[378,1117,1118],{"class":431},": { ",[378,1120,1121],{"class":1037},"node-version",[378,1123,1041],{"class":431},[378,1125,1126],{"class":391},"22",[378,1128,459],{"class":431},[378,1130,1131],{"class":1037},"cache",[378,1133,1041],{"class":431},[378,1135,384],{"class":387},[378,1137,1138],{"class":431}," }\n",[378,1140,1142,1144,1147,1149],{"class":162,"line":1141},11,[378,1143,1091],{"class":431},[378,1145,1146],{"class":1037},"run",[378,1148,1041],{"class":431},[378,1150,1151],{"class":387},"npm ci\n",[378,1153,1155,1157,1159,1161],{"class":162,"line":1154},12,[378,1156,1091],{"class":431},[378,1158,1146],{"class":1037},[378,1160,1041],{"class":431},[378,1162,1163],{"class":387},"npm run build && node scripts\u002Froute-files.mjs\n",[378,1165,1167,1169,1171,1173],{"class":162,"line":1166},13,[378,1168,1091],{"class":431},[378,1170,1146],{"class":1037},[378,1172,1041],{"class":431},[378,1174,1175],{"class":387},"npx size-limit\n",[378,1177,1179],{"class":162,"line":1178},14,[378,1180,1181],{"class":404},"# trade-off: this fails the check but does not explain the change. Add the\n",[378,1183,1185],{"class":162,"line":1184},15,[378,1186,1187],{"class":404},"# per-PR comment from the next guide so authors see the diff without digging.\n",[15,1189,1190],{},"Expected outcome: a pull request that pushes any route over budget cannot merge without an explicit decision.",[15,1192,1193],{},[40,1194,47,1197,47,1200,47,1203,47,1205,47,1212,47,1215,47,1217,47,1221,47,1225,47,1228,47,1233,47,1239,47,1244,47,1248,47,1254,47,1257,47,1261,47,1263,47,1266,47,1269,47,1272,47,1275,47,1278,47,1281,47,1285,47,1287,47,1290,47,1293,47,1296,47,1300,47,1303,47,1307,47,1311,47],{"viewBox":1195,"width":43,"role":44,"ariaLabel":1196,"style":46},"0 0 760 341","Decision sequence for responding to a failed size-limit budget in a pull request.",[49,1198],{"className":1199,"x":53,"y":53,"width":43,"height":43,"fill":54},[52],[56,1201,1202],{},"A budget failed — what now?",[60,1204,1196],{},[63,1206,1207],{},[66,1208,1210],{"id":1209,"viewBox":69,"refX":70,"refY":71,"markerWidth":72,"markerHeight":72,"orient":73},"faf35fdab9",[75,1211],{"d":77,"fill":78,"style":79},[49,1213],{"x":82,"y":82,"width":83,"height":1214,"rx":85,"fill":86,"stroke":78,"style":87},"339",[89,1216,1202],{"x":91,"y":92,"fill":78,"style":93},[49,1218],{"x":91,"y":96,"width":1219,"height":1220,"rx":99,"fill":78,"stroke":78,"style":267},"320.0","35.0",[89,1222,1224],{"x":1223,"y":105,"fill":78,"style":106,"textAnchor":107},"188.0","Is the new code needed on this route's first load?",[49,1226],{"x":1227,"y":96,"width":1219,"height":1220,"rx":99,"fill":100,"stroke":100,"style":101},"412.0",[89,1229,1232],{"x":1230,"y":105,"fill":78,"style":1231,"textAnchor":107},"572.0","font-size:12.5px","If not, move it behind import() — budget passes",[162,1234],{"x1":1235,"y1":1236,"x2":1237,"y2":1236,"stroke":78,"strokeWidth":167,"style":168,"markerEnd":1238},"348.0","73.5","410.0","url(#faf35fdab9)",[89,1240,1243],{"x":132,"y":1241,"fill":100,"style":1242,"textAnchor":107},"66.5","font-size:11.5px;font-weight:700","yes",[162,1245],{"x1":1223,"y1":1246,"x2":1223,"y2":1247,"stroke":78,"strokeWidth":167,"style":168,"markerEnd":1238},"91.0","125.0",[89,1249,1253],{"x":1250,"y":1251,"fill":1252,"style":1242},"196.0","113.0","#51617a","no",[49,1255],{"x":91,"y":1256,"width":1219,"height":1220,"rx":99,"fill":78,"stroke":78,"style":267},"127.0",[89,1258,1260],{"x":1223,"y":1259,"fill":78,"style":106,"textAnchor":107},"149.0","Is there a lighter alternative dependency?",[49,1262],{"x":1227,"y":1256,"width":1219,"height":1220,"rx":99,"fill":100,"stroke":100,"style":101},[89,1264,1265],{"x":1230,"y":1259,"fill":78,"style":1231,"textAnchor":107},"Swap it — and record the saving",[162,1267],{"x1":1235,"y1":1268,"x2":1237,"y2":1268,"stroke":78,"strokeWidth":167,"style":168,"markerEnd":1238},"144.5",[89,1270,1243],{"x":132,"y":1271,"fill":100,"style":1242,"textAnchor":107},"137.5",[162,1273],{"x1":1223,"y1":1274,"x2":1223,"y2":1250,"stroke":78,"strokeWidth":167,"style":168,"markerEnd":1238},"162.0",[89,1276,1253],{"x":1250,"y":1277,"fill":1252,"style":1242},"184.0",[49,1279],{"x":91,"y":1280,"width":1219,"height":1220,"rx":99,"fill":78,"stroke":78,"style":267},"198.0",[89,1282,1284],{"x":1223,"y":1283,"fill":78,"style":106,"textAnchor":107},"220.0","Can existing weight be removed to pay for it?",[49,1286],{"x":1227,"y":1280,"width":1219,"height":1220,"rx":99,"fill":100,"stroke":100,"style":101},[89,1288,1289],{"x":1230,"y":1283,"fill":78,"style":1231,"textAnchor":107},"Remove it in the same PR",[162,1291],{"x1":1235,"y1":1292,"x2":1237,"y2":1292,"stroke":78,"strokeWidth":167,"style":168,"markerEnd":1238},"215.5",[89,1294,1243],{"x":132,"y":1295,"fill":100,"style":1242,"textAnchor":107},"208.5",[162,1297],{"x1":1223,"y1":1298,"x2":1223,"y2":1299,"stroke":78,"strokeWidth":167,"style":168,"markerEnd":1238},"233.0","267.0",[89,1301,1253],{"x":1250,"y":1302,"fill":1252,"style":1242},"255.0",[49,1304],{"x":91,"y":1305,"width":1219,"height":1306,"rx":99,"fill":127,"stroke":128,"style":129},"269.0","50.0",[89,1308,1310],{"x":1223,"y":1309,"fill":78,"style":1231,"textAnchor":107},"290.5","Raise the budget with a written justification and owner",[89,1312,1314],{"x":1223,"y":1313,"fill":78,"style":1231,"textAnchor":107},"306.5","sign-off",[364,1316,1318],{"id":1317},"_4-ratchet-budgets-down-after-improvements","4. Ratchet budgets down after improvements",[15,1320,1321],{},"When an optimisation lands, lower the relevant budget to the new size plus a small margin in the same pull request. Budgets that only ever go up stop meaning anything.",[183,1323,1325],{"id":1324},"verification","Verification",[15,1327,1328],{},"Introduce a deliberate regression on a branch — import a large library eagerly in the landing entry — and confirm the size check fails with a clear message naming the route. Then remove it and confirm the check passes. Check that the measured landing size roughly matches the transfer size in the Network panel for the same build (brotli) and the decoded size (raw); large discrepancies mean the budget covers the wrong files.",[183,1330,1332],{"id":1331},"worked-example-catching-a-locale-explosion","Worked Example: Catching a Locale Explosion",[15,1334,1335,1336,1339,1340,1343,1344,1347],{},"A team added date formatting to a booking widget with ",[30,1337,1338],{},"import { format } from 'date-fns'"," and, for translated month names, ",[30,1341,1342],{},"import * as locales from 'date-fns\u002Flocale'",". The pull request diff was three lines. The landing budget failed: raw initial JavaScript went from 298KB to 512KB, because the namespace import pulled every locale into the entry chunk. The failure message showed the route and the size jump; the author replaced the namespace import with a dynamic ",[30,1345,1346],{},"import(\\","date-fns\u002Flocale\u002F${lang}`)` for the user's language, and the budget passed at 301KB. Without the budget, the regression would have shipped to every page, adding roughly 20ms of parse time on mid-tier phones for a widget most users never opened.",[183,1349,1351],{"id":1350},"common-mistakes","Common Mistakes",[188,1353,1354,1364,1370,1380],{},[191,1355,1356,1363],{},[194,1357,1358,1359,1362],{},"Budgeting the whole ",[30,1360,1361],{},"dist\u002F"," folder."," A total over all chunks includes lazy routes most users never load; it hides regressions on the critical path and penalises adding lazy features.",[191,1365,1366,1369],{},[194,1367,1368],{},"Forgetting CSS."," Render-blocking CSS affects LCP as much as JavaScript affects INP; add CSS budgets for the main stylesheet.",[191,1371,1372,1379],{},[194,1373,1374,1375,1378],{},"Using ",[30,1376,1377],{},"@size-limit\u002Ftime"," as a gate."," Execution-time estimates vary with the CI machine; treat them as informational.",[191,1381,1382,1385,1386,1389,1390,1392],{},[194,1383,1384],{},"Letting the base branch drift above budget."," If ",[30,1387,1388],{},"main"," already fails, every PR fails and the check is ignored. Fix ",[30,1391,1388],{}," first.",[183,1394,1396],{"id":1395},"faq","FAQ",[1398,1399,1402,1406],"details",{"className":1400},[1401],"faq-item",[1403,1404,1405],"summary",{},"Can size-limit measure a library's cost for consumers?",[15,1407,1408,1409,1412,1413,1415,1416,1419],{},"Yes. With ",[30,1410,1411],{},"@size-limit\u002Fesbuild"," (or the webpack plugin) and the ",[30,1414,628],{}," option, it bundles a tiny entry that imports specific exports and measures the result — the cost a consumer pays for ",[30,1417,1418],{},"import { Button } from '@acme\u002Fui'",". That is the right budget for libraries, where tree-shakeability matters more than total file size.",[1398,1421,1423,1426],{"className":1422},[1401],[1403,1424,1425],{},"How tight should the margin above current size be?",[15,1427,1428],{},"Three to five percent for raw-size budgets on routes that change often, tighter for stable ones. The margin exists so routine fixes do not fail CI; anything larger lets meaningful regressions slip through unnoticed.",[1398,1430,1432,1435],{"className":1431},[1401],[1403,1433,1434],{},"Does size-limit work with webpack and Next.js?",[15,1436,1437],{},"Yes — it measures files, so any bundler's output works. For Next.js, budget the files listed in the build manifest for each page, or use the \"First Load JS\" figure Next.js prints, which is the per-route initial JavaScript total.",[183,1439,1441],{"id":1440},"related","Related",[188,1443,1444,1451,1458],{},[191,1445,1446,1450],{},[19,1447,1449],{"href":1448},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Ftracking-bundle-size-per-pull-request\u002F","Tracking bundle size per pull request"," — the reporting layer on top of these checks.",[191,1452,1453,1457],{},[19,1454,1456],{"href":1455},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fwhy-compressed-size-is-the-wrong-budget\u002F","Why compressed size is the wrong budget"," — why the raw budget matters.",[191,1459,1460,1464],{},[19,1461,1463],{"href":1462},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Freducing-vendor-chunk-size-in-a-react-app\u002F","Reducing vendor chunk size in a React app"," — what to do when the shared chunk is the problem.",[1466,1467,1469],"script",{"type":1468},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"How to Enforce JavaScript Bundle Budgets with size-limit\",\n  \"description\": \"Set up size-limit for per-route JavaScript budgets that fail the build, report clear diffs and stay accurate as chunking changes.\",\n  \"step\": [\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 1,\n      \"name\": \"Install and define budgets per route\",\n      \"text\": \"Expected outcome: npx size-limit prints each budget with its current size and fails if any is over.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 2,\n      \"name\": \"Generate route file lists from the manifest\",\n      \"text\": \"Globs are brittle; a small script can produce the exact file list for each entry from the build manifest and write it to a JSON file that .size-limit.cjs reads.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 3,\n      \"name\": \"Run in CI and fail on overage\",\n      \"text\": \"Expected outcome: a pull request that pushes any route over budget cannot merge without an explicit decision.\"\n    },\n    {\n      \"@type\": \"HowToStep\",\n      \"position\": 4,\n      \"name\": \"Ratchet budgets down after improvements\",\n      \"text\": \"When an optimisation lands, lower the relevant budget to the new size plus a small margin in the same pull request.\"\n    }\n  ]\n}\n",[1466,1471,1472],{"type":1468},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Enforce JavaScript Bundle Budgets with size-limit\",\n  \"description\": \"Set up size-limit for per-route JavaScript budgets that fail the build, report clear diffs and stay accurate as chunking changes.\",\n  \"datePublished\": \"2026-10-06\",\n  \"dateModified\": \"2026-10-06\",\n  \"author\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"publisher\": {\n    \"@type\": \"Organization\",\n    \"name\": \"frontend-performance.com\"\n  },\n  \"mainEntityOfPage\": {\n    \"@type\": \"WebPage\",\n    \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fenforcing-bundle-budgets-with-size-limit\u002F\"\n  }\n}\n",[1466,1474,1475],{"type":1468},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"BreadcrumbList\",\n  \"itemListElement\": [\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 1,\n      \"name\": \"Home\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 2,\n      \"name\": \"JavaScript Bundle Optimization & Code Splitting\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 3,\n      \"name\": \"JavaScript Performance Budgets\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002F\"\n    },\n    {\n      \"@type\": \"ListItem\",\n      \"position\": 4,\n      \"name\": \"Enforcing Bundle Budgets with size-limit\",\n      \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fenforcing-bundle-budgets-with-size-limit\u002F\"\n    }\n  ]\n}\n",[1477,1478,1479],"style",{},"html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html .default .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .shiki span {color: var(--shiki-default);background: var(--shiki-default-bg);font-style: var(--shiki-default-font-style);font-weight: var(--shiki-default-font-weight);text-decoration: var(--shiki-default-text-decoration);}html .dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html.dark .shiki span {color: var(--shiki-dark);background: var(--shiki-dark-bg);font-style: var(--shiki-dark-font-style);font-weight: var(--shiki-dark-font-weight);text-decoration: var(--shiki-dark-text-decoration);}html .light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html.light .shiki span {color: var(--shiki-light);background: var(--shiki-light-bg);font-style: var(--shiki-light-font-style);font-weight: var(--shiki-light-font-weight);text-decoration: var(--shiki-light-text-decoration);}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}",{"title":374,"searchDepth":401,"depth":401,"links":1481},[1482,1483,1484,1490,1491,1492,1493,1494],{"id":185,"depth":401,"text":186},{"id":218,"depth":401,"text":219},{"id":361,"depth":401,"text":362,"children":1485},[1486,1487,1488,1489],{"id":366,"depth":408,"text":367},{"id":605,"depth":408,"text":606},{"id":1019,"depth":408,"text":1020},{"id":1317,"depth":408,"text":1318},{"id":1324,"depth":401,"text":1325},{"id":1331,"depth":401,"text":1332},{"id":1350,"depth":401,"text":1351},{"id":1395,"depth":401,"text":1396},{"id":1440,"depth":401,"text":1441},"Set up size-limit for per-route JavaScript budgets that fail the build, report clear diffs and stay accurate as chunking changes.","md",{"slug":1498,"type":1499,"breadcrumb":1500,"datePublished":1508,"dateModified":1508},"enforcing-bundle-budgets-with-size-limit","article",[1501,1504,1505,1506],{"name":1502,"url":1503},"Home","\u002F",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":1507},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fenforcing-bundle-budgets-with-size-limit\u002F","2026-10-06",true,"\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fenforcing-bundle-budgets-with-size-limit",{"title":1512,"description":1513},"Enforcing JavaScript Bundle Budgets with size-limit","Configure size-limit to fail CI when an entry point or route exceeds its JavaScript budget, measure brotli and uncompressed size, and estimate execution time.","javascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fenforcing-bundle-budgets-with-size-limit\u002Findex","CuwhCjRUaZ8wNC0pYQBVqswwfO1Izaa1j_Kk0U2CsB8",[1517,1520],{"title":22,"path":1518,"stem":1519},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets","javascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Findex",{"title":1521,"path":1522,"stem":1523},"Measuring JavaScript Parse and Compile Cost","\u002Fjavascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fmeasuring-javascript-parse-and-compile-cost","javascript-bundle-optimization-code-splitting\u002Fjavascript-performance-budgets\u002Fmeasuring-javascript-parse-and-compile-cost\u002Findex",1791308077562]