[{"data":1,"prerenderedAt":1481},["ShallowReactive",2],{"content:\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Freducing-vendor-chunk-size-in-a-react-app":3,"surroundings:\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Freducing-vendor-chunk-size-in-a-react-app":1475},{"id":4,"title":5,"body":6,"description":1454,"extension":1455,"meta":1456,"navigation":1001,"path":1469,"seo":1470,"stem":1473,"__hash__":1474},"content\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Freducing-vendor-chunk-size-in-a-react-app\u002Findex.md","Reducing vendor chunk size in a React app",{"type":7,"value":8,"toc":1442},"minimark",[9,13,33,40,43,159,164,167,208,290,305,308,485,489,510,528,534,544,566,570,575,578,827,838,922,925,929,936,1100,1103,1107,1110,1154,1157,1161,1164,1242,1256,1260,1277,1310,1393,1396,1400,1433,1438],[10,11,5],"h1",{"id":12},"reducing-vendor-chunk-size-in-a-react-app",[14,15,16,17,22,23,27,28,32],"p",{},"This walkthrough sits beneath the ",[18,19,21],"a",{"href":20},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002F","webpack bundle analysis techniques"," guide and the broader ",[18,24,26],{"href":25},"\u002Fjavascript-bundle-optimization-code-splitting\u002F","JavaScript bundle optimization and code splitting"," effort, and it tackles a specific, common failure: a React app whose ",[29,30,31],"code",{},"vendor.js"," chunk has ballooned past 400KB gzipped, dragging initial load and inflating main-thread parse beyond the 50ms long-task budget.",[14,34,35,36,39],{},"A bloated vendor chunk is the single most common bundle pathology in React apps, because the default instinct — \"put all of ",[29,37,38],{},"node_modules"," in one chunk\" — quietly accumulates every dependency a single import ever pulled in. The symptom is a slow first paint and a sluggish first interaction; the cause is almost always two or three oversized libraries plus a cache-churn pattern that re-downloads the whole chunk on every deploy. This page moves from baseline measurement to root-cause isolation to numbered, byte-quantified fixes, then verifies the result.",[14,41,42],{},"Keep two numbers in view throughout. The first is the initial-route JavaScript budget: aim for the shared framework and vendor chunks to sum to under ~150KB gzipped on the first paint, because everything above that competes with your Largest Contentful Paint image for the same download window on mid-tier mobile. The second is the 50ms long-task budget: any single Evaluate Script task over 50ms blocks the main thread and shows up as input delay, which is why one giant vendor chunk hurts responsiveness even when the network is fast. The fixes below are ordered so that the change with the highest byte-for-effort ratio comes first.",[14,44,45],{},[46,47,54,55,54,59,54,63,54,73,54,80,54,86,54,94,54,100,54,105,54,109,54,114,54,119,54,123,54,130,54,134,54,137,54,141,54,145,54,148,54,154,54],"svg",{"xmlns":48,"viewBox":49,"width":50,"role":51,"ariaLabel":52,"style":53},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 312","100%","img","Before and after treemap of a 400KB monolithic vendor chunk split into framework, slim vendor, and a lazy route chunk","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:#001d3d"," ",[56,57,58],"title",{},"Splitting a monolithic vendor chunk",[60,61,62],"desc",{},"One 400KB vendor chunk versus a stable framework chunk, a slimmer vendor chunk, and a lazy route-only chunk.",[64,65],"rect",{"x":66,"y":66,"width":67,"height":68,"rx":69,"fill":70,"stroke":71,"style":72},"1","758","310","10","none","currentColor","stroke-opacity:0.18",[74,75,79],"text",{"x":76,"y":77,"fill":71,"style":78},"24","36","font-size:17px;font-weight:700","vendor.js: monolith to cache groups",[74,81,85],{"x":82,"y":83,"fill":71,"style":84},"40","76","font-size:13px;font-weight:600","Before",[64,87],{"x":82,"y":88,"width":89,"height":90,"rx":91,"fill":92,"stroke":92,"style":93},"88","560","56","5","#0466c8","fill-opacity:0.16",[74,95,99],{"x":96,"y":97,"fill":71,"style":98},"320","113","font-size:12px;font-weight:600;text-anchor:middle","vendor.js 400KB",[74,101,104],{"x":96,"y":102,"fill":71,"style":103},"132","font-size:11px;text-anchor:middle","re-downloaded on bump",[74,106,108],{"x":82,"y":107,"fill":71,"style":84},"184","After",[64,110],{"x":82,"y":111,"width":112,"height":90,"rx":91,"fill":71,"stroke":71,"style":113},"196","150","stroke-opacity:0.45;fill-opacity:0.08",[74,115,118],{"x":116,"y":117,"fill":71,"style":98},"115","220","framework",[74,120,122],{"x":116,"y":121,"fill":71,"style":103},"238","~45KB, stable",[64,124],{"x":125,"y":111,"width":126,"height":90,"rx":91,"fill":127,"stroke":128,"style":129},"200","190","#ffc300","#b8860b","fill-opacity:0.24",[74,131,133],{"x":132,"y":117,"fill":71,"style":98},"295","vendor (slim)",[74,135,136],{"x":132,"y":121,"fill":71,"style":103},"deduped, tree-shaken",[64,138],{"x":139,"y":111,"width":125,"height":90,"rx":91,"fill":92,"stroke":92,"style":140},"400","fill-opacity:0.1;stroke-dasharray:5 4",[74,142,144],{"x":143,"y":117,"fill":71,"style":98},"500","lazy route chunk",[74,146,147],{"x":143,"y":121,"fill":71,"style":103},"chart, loads on route",[149,150],"line",{"x1":76,"y1":151,"x2":152,"y2":151,"stroke":71,"style":153},"268","736","stroke-opacity:0.3",[74,155,158],{"x":76,"y":156,"fill":71,"style":157},"290","font-size:12px","Isolate the runtime so a deploy no longer invalidates the whole vendor.",[160,161,163],"h2",{"id":162},"rapid-diagnosis-confirming-the-vendor-chunk-is-the-problem","Rapid diagnosis: confirming the vendor chunk is the problem",[14,165,166],{},"Before changing config, prove the vendor chunk is the bottleneck and find what is inside it. Run through this DevTools and analyzer checklist:",[168,169,170,182,188,194],"ul",{},[171,172,173,177,178,181],"li",{},[174,175,176],"strong",{},"Network tab (Disable cache, Fast 3G throttle):"," sort by transfer size. If ",[29,179,180],{},"vendor.[hash].js"," is the largest resource and blocks the route, it is your target.",[171,183,184,187],{},[174,185,186],{},"Coverage tab:"," record a page load and read the unused-bytes percentage for the vendor chunk. Above ~40% unused means dead weight is shipping.",[171,189,190,193],{},[174,191,192],{},"Performance panel:"," look for a single Compile\u002FEvaluate Script task over 50ms — that long task is the vendor chunk parsing on the main thread.",[171,195,196,202,203,207],{},[174,197,198,201],{},[29,199,200],{},"webpack-bundle-analyzer",":"," generate the treemap and note the three biggest rectangles. Those three libraries are where 80% of your savings live. (See ",[18,204,206],{"href":205},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fhow-to-configure-webpack-bundle-analyzer-for-production\u002F","how to configure webpack bundle analyzer for production"," if you have not set this up.)",[209,210,215],"pre",{"className":211,"code":212,"language":213,"meta":214,"style":214},"language-bash shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","# Produce a static treemap from your production stats\nnpx webpack --mode production --json > stats.json\nnpx webpack-bundle-analyzer stats.json dist --mode static --report report.html\n# trade-off: analyze a PRODUCTION build only — dev builds include HMR runtime\n# and unminified deps, so their treemap proportions mislead you.\n","bash","",[29,216,217,225,253,278,284],{"__ignoreMap":214},[218,219,221],"span",{"class":149,"line":220},1,[218,222,224],{"class":223},"sIIH1","# Produce a static treemap from your production stats\n",[218,226,228,232,236,240,243,246,250],{"class":149,"line":227},2,[218,229,231],{"class":230},"seIZK","npx",[218,233,235],{"class":234},"s-_DF"," webpack",[218,237,239],{"class":238},"sf6mN"," --mode",[218,241,242],{"class":234}," production",[218,244,245],{"class":238}," --json",[218,247,249],{"class":248},"sP5qI"," >",[218,251,252],{"class":234}," stats.json\n",[218,254,256,258,261,264,267,269,272,275],{"class":149,"line":255},3,[218,257,231],{"class":230},[218,259,260],{"class":234}," webpack-bundle-analyzer",[218,262,263],{"class":234}," stats.json",[218,265,266],{"class":234}," dist",[218,268,239],{"class":238},[218,270,271],{"class":234}," static",[218,273,274],{"class":238}," --report",[218,276,277],{"class":234}," report.html\n",[218,279,281],{"class":149,"line":280},4,[218,282,283],{"class":223},"# trade-off: analyze a PRODUCTION build only — dev builds include HMR runtime\n",[218,285,287],{"class":149,"line":286},5,[218,288,289],{"class":223},"# and unminified deps, so their treemap proportions mislead you.\n",[14,291,292,293,297,298,300,301,304],{},"Read the treemap by ",[294,295,296],"em",{},"gzipped"," (or parsed) size, not stat size: ",[29,299,200],{}," defaults to showing stat sizes, which overstate the byte cost of anything that compresses well and understate the parse cost of what does not. Toggle the sidebar to \"Gzipped\" so the rectangles reflect what actually crosses the wire. For a second opinion that maps bytes back to your original modules, run ",[29,302,303],{},"npx source-map-explorer dist\u002Fvendor.*.js"," — it attributes each byte to a source file, which is invaluable when a single transitive dependency is hiding inside a rectangle labelled only with the top-level package name.",[14,306,307],{},"This diagram maps each root cause below to the fix that addresses it, so you can jump straight to the treatment once the treemap names your culprit.",[46,309,312,313,312,316,312,319,312,322,312,326,312,331,312,335,312,358,312,374,312,437],{"xmlns":48,"viewBox":310,"width":50,"role":51,"ariaLabel":311,"style":53},"0 0 760 372","Mapping of five vendor-bloat failure modes to the four numbered fixes that resolve them, with connectors crossing from each cause on the left to its fix on the right","\n  ",[56,314,315],{},"Which vendor-bloat cause maps to which fix",[60,317,318],{},"The five treemap symptoms — node_modules catch-all, namespace import, route-only heavy dependency, duplicate copies, and a CommonJS-only library — connected to the four numbered fixes: cache groups, lazy route chunk, named imports, and dedupe\u002Fright-size.",[64,320],{"x":66,"y":66,"width":67,"height":321,"rx":69,"fill":70,"stroke":71,"style":72},"370",[74,323,325],{"x":76,"y":324,"fill":71,"style":78},"34","Match each vendor-bloat cause to its fix",[74,327,330],{"x":82,"y":328,"fill":71,"style":329},"66","font-size:12px;font-weight:600;letter-spacing:0.03em;text-transform:uppercase","Failure mode (treemap symptom)",[74,332,334],{"x":333,"y":328,"fill":71,"style":329},"494","Numbered fix",[336,337,340,341,340,346,340,349,340,352,340,355,312],"g",{"fill":70,"stroke":71,"strokeWidth":338,"style":339},"1.5","stroke-opacity:0.42","\n    ",[342,343],"path",{"d":344,"markerEnd":345},"M278 101 C380 101 380 131 476 131","url(#fpArrow)",[342,347],{"d":348,"markerEnd":345},"M278 159 C380 159 380 251 476 251",[342,350],{"d":351,"markerEnd":345},"M278 217 C380 217 380 191 476 191",[342,353],{"d":354,"markerEnd":345},"M278 275 C380 275 380 311 476 311",[342,356],{"d":357,"markerEnd":345},"M278 333 C380 333 380 311 476 311",[359,360,340,361,312],"defs",{},[362,363,369,370,340],"marker",{"id":364,"viewBox":365,"refX":366,"refY":91,"markerWidth":367,"markerHeight":367,"orient":368},"fpArrow","0 0 10 10","8","7","auto-start-reverse","\n      ",[342,371],{"d":372,"fill":71,"style":373},"M0 0 L10 5 L0 10 z","fill-opacity:0.55",[336,375,340,377,340,385,340,389,340,394,340,397,340,401,340,405,340,407,340,411,340,415,340,418,340,422,340,426,340,429,340,433,312],{"style":376},"font-size:12.5px;font-weight:600",[64,378],{"x":379,"y":380,"width":381,"height":382,"rx":383,"fill":71,"stroke":71,"style":384},"28","80","250","42","6","fill-opacity:0.04;stroke-opacity:0.34",[74,386,388],{"x":382,"y":387,"fill":71},"98","1  node_modules catch-all",[74,390,393],{"x":382,"y":391,"fill":71,"style":392},"114","font-size:11px;font-weight:400","one content hash for every dep",[64,395],{"x":379,"y":396,"width":381,"height":382,"rx":383,"fill":71,"stroke":71,"style":384},"138",[74,398,400],{"x":382,"y":399,"fill":71},"156","2  Namespace import",[74,402,404],{"x":382,"y":403,"fill":71,"style":392},"172","import * as Icons from …",[64,406],{"x":379,"y":111,"width":381,"height":382,"rx":383,"fill":71,"stroke":71,"style":384},[74,408,410],{"x":382,"y":409,"fill":71},"214","3  Route-only heavy dep",[74,412,414],{"x":382,"y":413,"fill":71,"style":392},"230","charting lib ships to every page",[64,416],{"x":379,"y":417,"width":381,"height":382,"rx":383,"fill":71,"stroke":71,"style":384},"254",[74,419,421],{"x":382,"y":420,"fill":71},"272","4  Duplicate copies",[74,423,425],{"x":382,"y":424,"fill":71,"style":392},"288","two versions in one chunk",[64,427],{"x":379,"y":428,"width":381,"height":382,"rx":383,"fill":71,"stroke":71,"style":384},"312",[74,430,432],{"x":382,"y":431,"fill":71},"330","5  CommonJS-only lib",[74,434,436],{"x":382,"y":435,"fill":71,"style":392},"346","exports cannot be shaken",[336,438,340,439,340,444,340,449,340,453,340,456,340,460,340,464,340,466,340,470,340,474,340,477,340,481,312],{"style":376},[64,440],{"x":441,"y":442,"width":381,"height":382,"rx":383,"fill":92,"stroke":92,"style":443},"482","110","fill-opacity:0.1;stroke-opacity:0.5",[74,445,448],{"x":446,"y":447,"fill":71},"496","128","Fix 1 — cache groups",[74,450,452],{"x":446,"y":451,"fill":71,"style":392},"144","split framework vs vendor",[64,454],{"x":441,"y":455,"width":381,"height":382,"rx":383,"fill":92,"stroke":92,"style":443},"170",[74,457,459],{"x":446,"y":458,"fill":71},"188","Fix 2 — lazy route chunk",[74,461,463],{"x":446,"y":462,"fill":71,"style":392},"204","React.lazy on the route",[64,465],{"x":441,"y":413,"width":381,"height":382,"rx":383,"fill":92,"stroke":92,"style":443},[74,467,469],{"x":446,"y":468,"fill":71},"248","Fix 3 — named imports",[74,471,473],{"x":446,"y":472,"fill":71,"style":392},"264","let tree-shaking prune",[64,475],{"x":441,"y":156,"width":381,"height":382,"rx":383,"fill":127,"stroke":128,"style":476},"fill-opacity:0.22",[74,478,480],{"x":446,"y":479,"fill":71},"308","Fix 4 — dedupe \u002F right-size",[74,482,484],{"x":446,"y":483,"fill":71,"style":392},"324","alias one copy, swap heavy libs",[160,486,488],{"id":487},"root-cause-analysis-why-vendor-chunks-balloon-in-react-apps","Root cause analysis: why vendor chunks balloon in React apps",[14,490,491,497,498,501,502,505,506,509],{},[174,492,493,494,496],{},"Failure mode 1 — the monolithic ",[29,495,38],{}," catch-all."," A single ",[29,499,500],{},"cacheGroups"," rule with ",[29,503,504],{},"test: \u002Fnode_modules\u002F"," lumps React, your UI kit, your charting library, and a date library into one file. Any version bump to ",[294,507,508],{},"any"," dependency invalidates the entire chunk's hash, so returning users re-download all 400KB even though only one library changed.",[14,511,512,54,515,518,519,522,523,527],{},[174,513,514],{},"Failure mode 2 — a heavy library imported at the namespace level.",[29,516,517],{},"import * as Icons from 'react-icons'"," or ",[29,520,521],{},"import _ from 'lodash'"," pulls the whole package because the import defeats static analysis. The mechanism is the same dead-code barrier covered in ",[18,524,526],{"href":525},"\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002Ffixing-tree-shaking-issues-with-lodash-and-moment\u002F","fixing tree-shaking issues with lodash and moment",".",[14,529,530,533],{},[174,531,532],{},"Failure mode 3 — an always-loaded heavy dependency that only one route needs."," A charting library (often 150KB+) or a rich-text editor lives in the vendor chunk and ships to every user, even those who never open the dashboard route that uses it. It belongs in a route-level dynamic import, not the shared vendor file.",[14,535,536,539,540,543],{},[174,537,538],{},"Failure mode 4 — duplicate copies of the same dependency."," Two transitive deps requesting different minor versions of, say, a polyfill produce two copies inside vendor. The treemap shows the same package name twice — pure waste. The mechanism is npm's dependency resolution: when two ranges are incompatible, npm nests a second physical copy under ",[29,541,542],{},"node_modules\u002F\u003Cdep>\u002Fnode_modules\u002F",", and webpack faithfully bundles both.",[14,545,546,549,550,553,554,557,558,561,562,565],{},[174,547,548],{},"Failure mode 5 — a CommonJS-only library that cannot be shaken."," Even a correctly named import (",[29,551,552],{},"import { debounce } from 'some-lib'",") ships the whole package if that package publishes only CommonJS or forgets ",[29,555,556],{},"\"sideEffects\": false"," in its ",[29,559,560],{},"package.json",". Webpack cannot statically prove which exports are unused across a CommonJS ",[29,563,564],{},"require"," graph, so the entire module survives minification — the same barrier described in the tree-shaking guide linked above, but here it silently inflates vendor rather than a feature chunk.",[160,567,569],{"id":568},"step-by-step-resolution-numbered-fixes-by-impact","Step-by-step resolution: numbered fixes by impact",[571,572,574],"h3",{"id":573},"_1-split-the-monolith-into-stable-cache-groups","1. Split the monolith into stable cache groups",[14,576,577],{},"Carve the rarely-changing framework core away from volatile app dependencies so a single dependency bump no longer invalidates everything.",[209,579,583],{"className":580,"code":581,"language":582,"meta":214,"style":214},"language-js shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","\u002F\u002F webpack.config.js\nmodule.exports = {\n  optimization: {\n    runtimeChunk: 'single', \u002F\u002F isolate the webpack runtime so its hash churn\n                            \u002F\u002F doesn't invalidate vendor on every build\n    splitChunks: {\n      chunks: 'all',\n      cacheGroups: {\n        framework: {\n          test: \u002F[\\\\\u002F]node_modules[\\\\\u002F](react|react-dom|scheduler)[\\\\\u002F]\u002F,\n          name: 'framework', priority: 40, enforce: true,\n        },\n        vendor: {\n          test: \u002F[\\\\\u002F]node_modules[\\\\\u002F]\u002F,\n          name: 'vendor', priority: 10,\n        },\n      },\n    },\n  },\n  \u002F\u002F trade-off: more chunks means more HTTP requests; on HTTP\u002F1.1 origins the\n  \u002F\u002F request overhead can outweigh the cache-stability win, so don't over-split.\n};\n","js",[29,584,585,590,607,612,626,631,637,649,655,661,713,735,741,747,772,786,791,797,803,809,815,821],{"__ignoreMap":214},[218,586,587],{"class":149,"line":220},[218,588,589],{"class":223},"\u002F\u002F webpack.config.js\n",[218,591,592,595,598,601,604],{"class":149,"line":227},[218,593,594],{"class":238},"module",[218,596,527],{"class":597},"syybb",[218,599,600],{"class":238},"exports",[218,602,603],{"class":248}," =",[218,605,606],{"class":597}," {\n",[218,608,609],{"class":149,"line":255},[218,610,611],{"class":597},"  optimization: {\n",[218,613,614,617,620,623],{"class":149,"line":280},[218,615,616],{"class":597},"    runtimeChunk: ",[218,618,619],{"class":234},"'single'",[218,621,622],{"class":597},", ",[218,624,625],{"class":223},"\u002F\u002F isolate the webpack runtime so its hash churn\n",[218,627,628],{"class":149,"line":286},[218,629,630],{"class":223},"                            \u002F\u002F doesn't invalidate vendor on every build\n",[218,632,634],{"class":149,"line":633},6,[218,635,636],{"class":597},"    splitChunks: {\n",[218,638,640,643,646],{"class":149,"line":639},7,[218,641,642],{"class":597},"      chunks: ",[218,644,645],{"class":234},"'all'",[218,647,648],{"class":597},",\n",[218,650,652],{"class":149,"line":651},8,[218,653,654],{"class":597},"      cacheGroups: {\n",[218,656,658],{"class":149,"line":657},9,[218,659,660],{"class":597},"        framework: {\n",[218,662,664,667,670,673,677,680,682,684,686,688,691,694,697,699,702,704,706,708,711],{"class":149,"line":663},10,[218,665,666],{"class":597},"          test:",[218,668,669],{"class":234}," \u002F",[218,671,672],{"class":238},"[",[218,674,676],{"class":675},"s6uau","\\\\",[218,678,679],{"class":238},"\u002F]",[218,681,38],{"class":234},[218,683,672],{"class":238},[218,685,676],{"class":675},[218,687,679],{"class":238},[218,689,690],{"class":234},"(react",[218,692,693],{"class":248},"|",[218,695,696],{"class":234},"react-dom",[218,698,693],{"class":248},[218,700,701],{"class":234},"scheduler)",[218,703,672],{"class":238},[218,705,676],{"class":675},[218,707,679],{"class":238},[218,709,710],{"class":234},"\u002F",[218,712,648],{"class":597},[218,714,716,719,722,725,727,730,733],{"class":149,"line":715},11,[218,717,718],{"class":597},"          name: ",[218,720,721],{"class":234},"'framework'",[218,723,724],{"class":597},", priority: ",[218,726,82],{"class":238},[218,728,729],{"class":597},", enforce: ",[218,731,732],{"class":238},"true",[218,734,648],{"class":597},[218,736,738],{"class":149,"line":737},12,[218,739,740],{"class":597},"        },\n",[218,742,744],{"class":149,"line":743},13,[218,745,746],{"class":597},"        vendor: {\n",[218,748,750,752,754,756,758,760,762,764,766,768,770],{"class":149,"line":749},14,[218,751,666],{"class":597},[218,753,669],{"class":234},[218,755,672],{"class":238},[218,757,676],{"class":675},[218,759,679],{"class":238},[218,761,38],{"class":234},[218,763,672],{"class":238},[218,765,676],{"class":675},[218,767,679],{"class":238},[218,769,710],{"class":234},[218,771,648],{"class":597},[218,773,775,777,780,782,784],{"class":149,"line":774},15,[218,776,718],{"class":597},[218,778,779],{"class":234},"'vendor'",[218,781,724],{"class":597},[218,783,69],{"class":238},[218,785,648],{"class":597},[218,787,789],{"class":149,"line":788},16,[218,790,740],{"class":597},[218,792,794],{"class":149,"line":793},17,[218,795,796],{"class":597},"      },\n",[218,798,800],{"class":149,"line":799},18,[218,801,802],{"class":597},"    },\n",[218,804,806],{"class":149,"line":805},19,[218,807,808],{"class":597},"  },\n",[218,810,812],{"class":149,"line":811},20,[218,813,814],{"class":223},"  \u002F\u002F trade-off: more chunks means more HTTP requests; on HTTP\u002F1.1 origins the\n",[218,816,818],{"class":149,"line":817},21,[218,819,820],{"class":223},"  \u002F\u002F request overhead can outweigh the cache-stability win, so don't over-split.\n",[218,822,824],{"class":149,"line":823},22,[218,825,826],{"class":597},"};\n",[14,828,829,830,833,834,837],{},"The split alone is not enough to make the framework chunk actually cacheable. Two more settings finish the job: ",[29,831,832],{},"moduleIds: 'deterministic'"," freezes each module's internal id so an unrelated code change elsewhere does not renumber modules and shift the framework chunk's contents, and a ",[29,835,836],{},"[contenthash]"," filename ties the cache key to the bytes rather than to the build. Together they let a returning visitor keep the framework chunk in cache across dozens of deploys.",[209,839,841],{"className":580,"code":840,"language":582,"meta":214,"style":214},"\u002F\u002F webpack.config.js — make the stable chunk genuinely long-term cacheable\nmodule.exports = {\n  output: {\n    filename: '[name].[contenthash].js', \u002F\u002F hash follows content, not build number\n  },\n  optimization: {\n    moduleIds: 'deterministic', \u002F\u002F stable ids so unrelated edits don't reshuffle\n  },\n  \u002F\u002F trade-off: contenthash filenames require hashed asset references (they are\n  \u002F\u002F fingerprinted), so you must serve them through the generated HTML\u002Fmanifest —\n  \u002F\u002F never hardcode a chunk filename anywhere it could go stale.\n};\n",[29,842,843,848,860,865,878,882,886,899,903,908,913,918],{"__ignoreMap":214},[218,844,845],{"class":149,"line":220},[218,846,847],{"class":223},"\u002F\u002F webpack.config.js — make the stable chunk genuinely long-term cacheable\n",[218,849,850,852,854,856,858],{"class":149,"line":227},[218,851,594],{"class":238},[218,853,527],{"class":597},[218,855,600],{"class":238},[218,857,603],{"class":248},[218,859,606],{"class":597},[218,861,862],{"class":149,"line":255},[218,863,864],{"class":597},"  output: {\n",[218,866,867,870,873,875],{"class":149,"line":280},[218,868,869],{"class":597},"    filename: ",[218,871,872],{"class":234},"'[name].[contenthash].js'",[218,874,622],{"class":597},[218,876,877],{"class":223},"\u002F\u002F hash follows content, not build number\n",[218,879,880],{"class":149,"line":286},[218,881,808],{"class":597},[218,883,884],{"class":149,"line":633},[218,885,611],{"class":597},[218,887,888,891,894,896],{"class":149,"line":639},[218,889,890],{"class":597},"    moduleIds: ",[218,892,893],{"class":234},"'deterministic'",[218,895,622],{"class":597},[218,897,898],{"class":223},"\u002F\u002F stable ids so unrelated edits don't reshuffle\n",[218,900,901],{"class":149,"line":651},[218,902,808],{"class":597},[218,904,905],{"class":149,"line":657},[218,906,907],{"class":223},"  \u002F\u002F trade-off: contenthash filenames require hashed asset references (they are\n",[218,909,910],{"class":149,"line":663},[218,911,912],{"class":223},"  \u002F\u002F fingerprinted), so you must serve them through the generated HTML\u002Fmanifest —\n",[218,914,915],{"class":149,"line":715},[218,916,917],{"class":223},"  \u002F\u002F never hardcode a chunk filename anywhere it could go stale.\n",[218,919,920],{"class":149,"line":737},[218,921,826],{"class":597},[14,923,924],{},"Expected outcome: isolates ~45KB of stable framework code into a long-lived chunk; combined with deterministic ids, reduces the bytes re-downloaded per deploy by the framework's share (often ~30-40% of repeat-visit transfer), since the framework hash now survives builds that only touch application code.",[571,926,928],{"id":927},"_2-lazy-load-route-only-heavy-dependencies","2. Lazy-load route-only heavy dependencies",[14,930,931,932,935],{},"Move the charting library, editor, or map component out of vendor and behind ",[29,933,934],{},"React.lazy"," so it loads only on the route that uses it.",[209,937,941],{"className":938,"code":939,"language":940,"meta":214,"style":214},"language-jsx shiki shiki-themes github-light-high-contrast github-light-high-contrast github-light-high-contrast","import { lazy, Suspense } from 'react';\n\u002F\u002F Chart (and its ~150KB dep tree) now splits into its own async chunk.\nconst Dashboard = lazy(() => import('.\u002Froutes\u002FDashboard'));\n\nexport function Routes() {\n  return (\n    \u003CSuspense fallback={\u003CSpinner \u002F>}>\n      \u003CDashboard \u002F>\n    \u003C\u002FSuspense>\n  );\n  \u002F\u002F trade-off: lazy boundaries add a loading state and a round-trip on first\n  \u002F\u002F navigation; don't lazy-load components used on the very first paint or you\n  \u002F\u002F trade a smaller vendor chunk for a worse LCP.\n}\n","jsx",[29,942,943,960,965,997,1003,1017,1025,1055,1066,1075,1080,1085,1090,1095],{"__ignoreMap":214},[218,944,945,948,951,954,957],{"class":149,"line":220},[218,946,947],{"class":248},"import",[218,949,950],{"class":597}," { lazy, Suspense } ",[218,952,953],{"class":248},"from",[218,955,956],{"class":234}," 'react'",[218,958,959],{"class":597},";\n",[218,961,962],{"class":149,"line":227},[218,963,964],{"class":223},"\u002F\u002F Chart (and its ~150KB dep tree) now splits into its own async chunk.\n",[218,966,967,970,973,975,979,982,985,988,991,994],{"class":149,"line":255},[218,968,969],{"class":248},"const",[218,971,972],{"class":238}," Dashboard",[218,974,603],{"class":248},[218,976,978],{"class":977},"ssM3C"," lazy",[218,980,981],{"class":597},"(() ",[218,983,984],{"class":248},"=>",[218,986,987],{"class":248}," import",[218,989,990],{"class":597},"(",[218,992,993],{"class":234},"'.\u002Froutes\u002FDashboard'",[218,995,996],{"class":597},"));\n",[218,998,999],{"class":149,"line":280},[218,1000,1002],{"emptyLinePlaceholder":1001},true,"\n",[218,1004,1005,1008,1011,1014],{"class":149,"line":286},[218,1006,1007],{"class":248},"export",[218,1009,1010],{"class":248}," function",[218,1012,1013],{"class":977}," Routes",[218,1015,1016],{"class":597},"() {\n",[218,1018,1019,1022],{"class":149,"line":633},[218,1020,1021],{"class":248},"  return",[218,1023,1024],{"class":597}," (\n",[218,1026,1027,1030,1034,1037,1040,1043,1046,1049,1052],{"class":149,"line":639},[218,1028,1029],{"class":597},"    \u003C",[218,1031,1033],{"class":1032},"s-fAs","Suspense",[218,1035,1036],{"class":238}," fallback",[218,1038,1039],{"class":248},"={",[218,1041,1042],{"class":597},"\u003C",[218,1044,1045],{"class":1032},"Spinner",[218,1047,1048],{"class":597}," \u002F>",[218,1050,1051],{"class":248},"}",[218,1053,1054],{"class":597},">\n",[218,1056,1057,1060,1063],{"class":149,"line":651},[218,1058,1059],{"class":597},"      \u003C",[218,1061,1062],{"class":1032},"Dashboard",[218,1064,1065],{"class":597}," \u002F>\n",[218,1067,1068,1071,1073],{"class":149,"line":657},[218,1069,1070],{"class":597},"    \u003C\u002F",[218,1072,1033],{"class":1032},[218,1074,1054],{"class":597},[218,1076,1077],{"class":149,"line":663},[218,1078,1079],{"class":597},"  );\n",[218,1081,1082],{"class":149,"line":715},[218,1083,1084],{"class":223},"  \u002F\u002F trade-off: lazy boundaries add a loading state and a round-trip on first\n",[218,1086,1087],{"class":149,"line":737},[218,1088,1089],{"class":223},"  \u002F\u002F navigation; don't lazy-load components used on the very first paint or you\n",[218,1091,1092],{"class":149,"line":743},[218,1093,1094],{"class":223},"  \u002F\u002F trade a smaller vendor chunk for a worse LCP.\n",[218,1096,1097],{"class":149,"line":749},[218,1098,1099],{"class":597},"}\n",[14,1101,1102],{},"Expected outcome: removes the route-specific library from every page's critical path. Moving a 150KB (≈45KB gzipped) charting lib out of vendor cuts the initial vendor transfer by roughly that amount.",[571,1104,1106],{"id":1105},"_3-convert-namespace-imports-to-nameddeep-imports","3. Convert namespace imports to named\u002Fdeep imports",[14,1108,1109],{},"Replace whole-package imports with named or path imports so tree-shaking can prune the unused surface.",[209,1111,1113],{"className":938,"code":1112,"language":940,"meta":214,"style":214},"\u002F\u002F BEFORE: pulls the entire icon set into vendor\n\u002F\u002F import * as Icons from 'react-icons\u002Ffa';\n\u002F\u002F AFTER: only the icons you use survive tree-shaking\nimport { FaUser, FaCog } from 'react-icons\u002Ffa';\n\u002F\u002F trade-off: deep\u002Fnamed imports rely on the package shipping ESM with\n\u002F\u002F sideEffects:false — if it ships CommonJS, this won't shake; replace the dep.\n",[29,1114,1115,1120,1125,1130,1144,1149],{"__ignoreMap":214},[218,1116,1117],{"class":149,"line":220},[218,1118,1119],{"class":223},"\u002F\u002F BEFORE: pulls the entire icon set into vendor\n",[218,1121,1122],{"class":149,"line":227},[218,1123,1124],{"class":223},"\u002F\u002F import * as Icons from 'react-icons\u002Ffa';\n",[218,1126,1127],{"class":149,"line":255},[218,1128,1129],{"class":223},"\u002F\u002F AFTER: only the icons you use survive tree-shaking\n",[218,1131,1132,1134,1137,1139,1142],{"class":149,"line":280},[218,1133,947],{"class":248},[218,1135,1136],{"class":597}," { FaUser, FaCog } ",[218,1138,953],{"class":248},[218,1140,1141],{"class":234}," 'react-icons\u002Ffa'",[218,1143,959],{"class":597},[218,1145,1146],{"class":149,"line":286},[218,1147,1148],{"class":223},"\u002F\u002F trade-off: deep\u002Fnamed imports rely on the package shipping ESM with\n",[218,1150,1151],{"class":149,"line":633},[218,1152,1153],{"class":223},"\u002F\u002F sideEffects:false — if it ships CommonJS, this won't shake; replace the dep.\n",[14,1155,1156],{},"Expected outcome: for an icon or utility library, typically reduces that library's vendor contribution from tens of KB to single-digit KB gzipped.",[571,1158,1160],{"id":1159},"_4-deduplicate-and-right-size-individual-libraries","4. Deduplicate and right-size individual libraries",[14,1162,1163],{},"Resolve duplicate copies and swap heavyweight libraries for lean equivalents.",[209,1165,1167],{"className":580,"code":1166,"language":582,"meta":214,"style":214},"\u002F\u002F webpack.config.js — force a single copy of a duplicated dependency\nmodule.exports = {\n  resolve: {\n    alias: {\n      \u002F\u002F collapse two requested ranges to one resolved copy\n      'date-fns': require.resolve('date-fns'),\n    },\n  },\n  \u002F\u002F trade-off: aliasing to one version can break a transitive dep that relied\n  \u002F\u002F on the other range's API — run the test suite after pinning.\n};\n",[29,1168,1169,1174,1186,1191,1196,1201,1220,1224,1228,1233,1238],{"__ignoreMap":214},[218,1170,1171],{"class":149,"line":220},[218,1172,1173],{"class":223},"\u002F\u002F webpack.config.js — force a single copy of a duplicated dependency\n",[218,1175,1176,1178,1180,1182,1184],{"class":149,"line":227},[218,1177,594],{"class":238},[218,1179,527],{"class":597},[218,1181,600],{"class":238},[218,1183,603],{"class":248},[218,1185,606],{"class":597},[218,1187,1188],{"class":149,"line":255},[218,1189,1190],{"class":597},"  resolve: {\n",[218,1192,1193],{"class":149,"line":280},[218,1194,1195],{"class":597},"    alias: {\n",[218,1197,1198],{"class":149,"line":286},[218,1199,1200],{"class":223},"      \u002F\u002F collapse two requested ranges to one resolved copy\n",[218,1202,1203,1206,1209,1212,1214,1217],{"class":149,"line":633},[218,1204,1205],{"class":234},"      'date-fns'",[218,1207,1208],{"class":597},": require.",[218,1210,1211],{"class":977},"resolve",[218,1213,990],{"class":597},[218,1215,1216],{"class":234},"'date-fns'",[218,1218,1219],{"class":597},"),\n",[218,1221,1222],{"class":149,"line":639},[218,1223,802],{"class":597},[218,1225,1226],{"class":149,"line":651},[218,1227,808],{"class":597},[218,1229,1230],{"class":149,"line":657},[218,1231,1232],{"class":223},"  \u002F\u002F trade-off: aliasing to one version can break a transitive dep that relied\n",[218,1234,1235],{"class":149,"line":663},[218,1236,1237],{"class":223},"  \u002F\u002F on the other range's API — run the test suite after pinning.\n",[218,1239,1240],{"class":149,"line":715},[218,1241,826],{"class":597},[14,1243,1244,1245,1248,1249,518,1252,1255],{},"Alongside this, swap ",[29,1246,1247],{},"moment"," (~18KB gzipped core, more with locales) for ",[29,1250,1251],{},"date-fns",[29,1253,1254],{},"dayjs"," (~2KB core). Expected outcome: deduplication removes the redundant copy outright; the moment→dayjs swap saves ~15KB gzipped.",[160,1257,1259],{"id":1258},"verification-proving-the-chunk-shrank-and-stayed-shrunk","Verification: proving the chunk shrank and stayed shrunk",[14,1261,1262,1263,1265,1266,1268,1269,1272,1273,1276],{},"Re-run the same diagnosis against the new build and capture a before\u002Fafter diff. A healthy result on a typical mid-size app looks like this: a single ",[29,1264,31],{}," of ~400KB gzipped that re-downloaded in full on every deploy becomes a stable ",[29,1267,118],{}," chunk (~45KB, cached across deploys), a slim ",[29,1270,1271],{},"vendor"," chunk (~120KB), and one lazy ",[29,1274,1275],{},"dashboard"," chunk (~45KB) that never touches the marketing routes — dropping first-route JavaScript from ~400KB to ~165KB gzipped and the repeat-visit transfer far lower still.",[168,1278,1279,1285,1295,1304],{},[171,1280,1281,1284],{},[174,1282,1283],{},"Before\u002Fafter analyzer treemap:"," the vendor rectangle should be visibly smaller and the framework chunk should appear separately. Confirm no duplicate package names remain.",[171,1286,1287,1290,1291,1294],{},[174,1288,1289],{},"Network transfer:"," with cache disabled, the sum of ",[29,1292,1293],{},"framework + vendor"," initial JS should land under the 150KB gzipped initial-route budget.",[171,1296,1297,1299,1300,527],{},[174,1298,192],{}," the previously >50ms vendor Evaluate Script task should now be split across smaller chunks, each under the long-task threshold — the same responsiveness win discussed in ",[18,1301,1303],{"href":1302},"\u002Fcore-web-vitals-measurement\u002Foptimizing-first-input-delay-fid\u002F","optimizing First Input Delay and INP",[171,1305,1306,1309],{},[174,1307,1308],{},"CI assertion:"," lock the win in so it cannot regress.",[209,1311,1313],{"className":580,"code":1312,"language":582,"meta":214,"style":214},"\u002F\u002F CI budget — fail the build if vendor grows past target (bytes, gzipped)\nmodule.exports = {\n  performance: {\n    hints: 'error',\n    maxAssetSize: 160_000,       \u002F\u002F per-asset cap\n    maxEntrypointSize: 170_000,  \u002F\u002F initial route budget\n  },\n  \u002F\u002F trade-off: hard error budgets can block urgent hotfixes that legitimately\n  \u002F\u002F add bytes; pair the gate with an explicit override label for emergencies.\n};\n",[29,1314,1315,1320,1332,1337,1347,1361,1375,1379,1384,1389],{"__ignoreMap":214},[218,1316,1317],{"class":149,"line":220},[218,1318,1319],{"class":223},"\u002F\u002F CI budget — fail the build if vendor grows past target (bytes, gzipped)\n",[218,1321,1322,1324,1326,1328,1330],{"class":149,"line":227},[218,1323,594],{"class":238},[218,1325,527],{"class":597},[218,1327,600],{"class":238},[218,1329,603],{"class":248},[218,1331,606],{"class":597},[218,1333,1334],{"class":149,"line":255},[218,1335,1336],{"class":597},"  performance: {\n",[218,1338,1339,1342,1345],{"class":149,"line":280},[218,1340,1341],{"class":597},"    hints: ",[218,1343,1344],{"class":234},"'error'",[218,1346,648],{"class":597},[218,1348,1349,1352,1355,1358],{"class":149,"line":286},[218,1350,1351],{"class":597},"    maxAssetSize: ",[218,1353,1354],{"class":238},"160_000",[218,1356,1357],{"class":597},",       ",[218,1359,1360],{"class":223},"\u002F\u002F per-asset cap\n",[218,1362,1363,1366,1369,1372],{"class":149,"line":633},[218,1364,1365],{"class":597},"    maxEntrypointSize: ",[218,1367,1368],{"class":238},"170_000",[218,1370,1371],{"class":597},",  ",[218,1373,1374],{"class":223},"\u002F\u002F initial route budget\n",[218,1376,1377],{"class":149,"line":639},[218,1378,808],{"class":597},[218,1380,1381],{"class":149,"line":651},[218,1382,1383],{"class":223},"  \u002F\u002F trade-off: hard error budgets can block urgent hotfixes that legitimately\n",[218,1385,1386],{"class":149,"line":657},[218,1387,1388],{"class":223},"  \u002F\u002F add bytes; pair the gate with an explicit override label for emergencies.\n",[218,1390,1391],{"class":149,"line":663},[218,1392,826],{"class":597},[14,1394,1395],{},"A field check closes the loop: watch your RUM p75 for the affected routes after the deploy. The repeat-visit transfer should drop (thanks to the stable framework chunk) and the first-interaction metrics should improve as the smaller chunks parse faster.",[160,1397,1399],{"id":1398},"related","Related",[168,1401,1402,1408,1414,1420,1427],{},[171,1403,1404,1407],{},[18,1405,1406],{"href":20},"Webpack bundle analysis techniques"," — the parent guide on measuring what is inside your chunks.",[171,1409,1410,1413],{},[18,1411,1412],{"href":205},"How to configure webpack bundle analyzer for production"," — generating the treemap this workflow depends on.",[171,1415,1416,1419],{},[18,1417,1418],{"href":525},"Fixing tree-shaking issues with lodash and moment"," — eliminating the namespace-import bloat behind failure mode 2.",[171,1421,1422,1426],{},[18,1423,1425],{"href":1424},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fdynamic-imports-and-route-based-splitting\u002Fvite-vs-webpack-bundle-splitting-performance\u002F","Vite vs webpack bundle splitting performance"," — how chunking control compares across bundlers.",[171,1428,1429,1432],{},[18,1430,1431],{"href":1302},"Optimizing First Input Delay and INP"," — why smaller, split chunks reduce parse-driven input delay.",[1434,1435,1437],"script",{"type":1436},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@graph\": [\n    {\n      \"@type\": \"TechArticle\",\n      \"headline\": \"Reducing vendor chunk size in a React app\",\n      \"description\": \"A diagnosis-to-verification workflow for shrinking an oversized webpack vendor chunk in a React application, with root causes, numbered fixes, and CI budgets.\",\n      \"datePublished\": \"2026-06-18\",\n      \"dateModified\": \"2026-07-18\",\n      \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Freducing-vendor-chunk-size-in-a-react-app\u002F\"\n    },\n    {\n      \"@type\": \"HowTo\",\n      \"name\": \"Reduce an oversized webpack vendor chunk in a React app\",\n      \"step\": [\n        { \"@type\": \"HowToStep\", \"position\": 1, \"name\": \"Split into stable cache groups\", \"text\": \"Separate the React framework core from volatile app dependencies and isolate the runtime chunk.\" },\n        { \"@type\": \"HowToStep\", \"position\": 2, \"name\": \"Lazy-load route-only heavy deps\", \"text\": \"Move charting, editor, or map libraries behind React.lazy so they load only on their route.\" },\n        { \"@type\": \"HowToStep\", \"position\": 3, \"name\": \"Convert namespace imports\", \"text\": \"Replace whole-package imports with named or deep imports so tree-shaking prunes unused code.\" },\n        { \"@type\": \"HowToStep\", \"position\": 4, \"name\": \"Deduplicate and right-size\", \"text\": \"Collapse duplicate dependency copies and swap heavy libraries for lean equivalents.\" },\n        { \"@type\": \"HowToStep\", \"position\": 5, \"name\": \"Verify and budget\", \"text\": \"Re-run the analyzer, confirm the initial JS budget, and add a CI size assertion to prevent regression.\" }\n      ]\n    },\n    {\n      \"@type\": \"BreadcrumbList\",\n      \"itemListElement\": [\n        { \"@type\": \"ListItem\", \"position\": 1, \"name\": \"Home\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002F\" },\n        { \"@type\": \"ListItem\", \"position\": 2, \"name\": \"JavaScript Bundle Optimization & Code Splitting\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002F\" },\n        { \"@type\": \"ListItem\", \"position\": 3, \"name\": \"Webpack Bundle Analysis Techniques\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002F\" },\n        { \"@type\": \"ListItem\", \"position\": 4, \"name\": \"Reducing vendor chunk size in a React app\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Freducing-vendor-chunk-size-in-a-react-app\u002F\" }\n      ]\n    }\n  ]\n}\n",[1439,1440,1441],"style",{},"html pre.shiki code .sIIH1, html code.shiki .sIIH1{--shiki-default:#66707B;--shiki-dark:#66707B;--shiki-light:#66707B}html pre.shiki code .seIZK, html code.shiki .seIZK{--shiki-default:#702C00;--shiki-dark:#702C00;--shiki-light:#702C00}html pre.shiki code .s-_DF, html code.shiki .s-_DF{--shiki-default:#032563;--shiki-dark:#032563;--shiki-light:#032563}html pre.shiki code .sf6mN, html code.shiki .sf6mN{--shiki-default:#023B95;--shiki-dark:#023B95;--shiki-light:#023B95}html pre.shiki code .sP5qI, html code.shiki .sP5qI{--shiki-default:#A0111F;--shiki-dark:#A0111F;--shiki-light:#A0111F}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 .syybb, html code.shiki .syybb{--shiki-default:#0E1116;--shiki-dark:#0E1116;--shiki-light:#0E1116}html pre.shiki code .s6uau, html code.shiki .s6uau{--shiki-default:#024C1A;--shiki-default-font-weight:bold;--shiki-dark:#024C1A;--shiki-dark-font-weight:bold;--shiki-light:#024C1A;--shiki-light-font-weight:bold}html pre.shiki code .ssM3C, html code.shiki .ssM3C{--shiki-default:#622CBC;--shiki-dark:#622CBC;--shiki-light:#622CBC}html pre.shiki code .s-fAs, html code.shiki .s-fAs{--shiki-default:#024C1A;--shiki-dark:#024C1A;--shiki-light:#024C1A}",{"title":214,"searchDepth":227,"depth":227,"links":1443},[1444,1445,1446,1452,1453],{"id":162,"depth":227,"text":163},{"id":487,"depth":227,"text":488},{"id":568,"depth":227,"text":569,"children":1447},[1448,1449,1450,1451],{"id":573,"depth":255,"text":574},{"id":927,"depth":255,"text":928},{"id":1105,"depth":255,"text":1106},{"id":1159,"depth":255,"text":1160},{"id":1258,"depth":227,"text":1259},{"id":1398,"depth":227,"text":1399},"A diagnosis-to-verification workflow for shrinking an oversized webpack vendor chunk in a React application.","md",{"slug":12,"type":1457,"breadcrumb":1458,"datePublished":1467,"dateModified":1468},"article",[1459,1461,1463,1465],{"name":1460,"url":710},"Home",{"name":1462,"url":25},"JavaScript Bundle Optimization & Code Splitting",{"name":1464,"url":20},"Webpack Bundle Analysis Techniques",{"name":5,"url":1466},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Freducing-vendor-chunk-size-in-a-react-app\u002F","2026-06-18","2026-07-18","\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Freducing-vendor-chunk-size-in-a-react-app",{"title":1471,"description":1472},"Reducing Vendor Chunk Size in a React App","Diagnose and shrink an oversized webpack vendor chunk in a React app: root causes, numbered fixes with expected byte savings, and budget verification.","javascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Freducing-vendor-chunk-size-in-a-react-app\u002Findex","HpWM-FNlcfpaB4OwgFTwoDiZ1MBW-bC4FqVoYBVJMEI",[1476,1480],{"title":1477,"path":1478,"stem":1479},"Configure webpack-bundle-analyzer for Production","\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fhow-to-configure-webpack-bundle-analyzer-for-production","javascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Fhow-to-configure-webpack-bundle-analyzer-for-production\u002Findex",null,1784398533804]