[{"data":1,"prerenderedAt":973},["ShallowReactive",2],{"content:\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle":3,"surroundings:\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle":965},{"id":4,"title":5,"body":6,"description":946,"extension":947,"meta":948,"navigation":959,"path":960,"seo":961,"stem":963,"__hash__":964},"content\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002Findex.md","Finding Duplicate Dependencies in a Monorepo Bundle",{"type":7,"value":8,"toc":938},"minimark",[9,14,38,41,46,83,218,222,236,242,253,263,267,272,371,374,380,440,478,481,490,561,564,570,716,720,723,726,811,818,822,834,848,873,882,891,900,904,923,928,931,934],[10,11,13],"h1",{"id":12},"how-to-find-duplicate-dependencies-in-a-monorepo-bundle","How to Find Duplicate Dependencies in a Monorepo Bundle",[15,16,17,18,23,24,28,29,33,34,37],"p",{},"This walkthrough extends ",[19,20,22],"a",{"href":21},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002F","Webpack Bundle Analysis Techniques"," inside ",[19,25,27],{"href":26},"\u002Fjavascript-bundle-optimization-code-splitting\u002F","JavaScript Bundle Optimization & Code Splitting",". The bundle report shows ",[30,31,32],"code",{},"date-fns"," three times, ",[30,35,36],{},"@internal\u002Fui"," twice, and a suspiciously large vendor chunk. In a workspace repository this is normal and it is not a bundler bug: two packages asked for incompatible ranges of the same dependency, the package manager installed both, and the bundler faithfully included each one.",[15,39,40],{},"Duplicates cost bytes, parse time, and correctness — two copies of a state library mean two independent stores.",[42,43,45],"h2",{"id":44},"rapid-diagnosis-confirm-and-quantify","Rapid Diagnosis: Confirm and Quantify",[47,48,49,57,71,77],"ul",{},[50,51,52,56],"li",{},[53,54,55],"strong",{},"Read the analyzer treemap for repeated names."," The same package name appearing under two different paths is the signal; the nesting tells you who pulled each copy.",[50,58,59,62,63,66,67,70],{},[53,60,61],{},"Ask the package manager first."," ",[30,64,65],{},"npm ls \u003Cpkg>"," or ",[30,68,69],{},"pnpm why \u003Cpkg>"," shows the resolution tree and the requested ranges — the actual cause.",[50,72,73,76],{},[53,74,75],{},"Separate real duplicates from re-exports."," A package appearing twice with different sizes is usually two versions; identical sizes under different paths can be a symlink or an alias artefact.",[50,78,79,82],{},[53,80,81],{},"Check for singleton violations."," React, state stores and context providers must be single instances. A duplicate here causes bugs, not just bytes: hooks fail, contexts read as undefined.",[84,85,92,93,62,100,62,104,62,108,62,117,62,124,62,131,62,139,62,144,62,151,62,155,62,158,62,162,62,168,62,172,62,174,62,177,62,181,62,187,62,191,62,194,62,196,62,199,62,202,62,205,62,209,62,213,62,215,62],"svg",{"xmlns":86,"viewBox":87,"width":88,"role":89,"ariaLabel":90,"style":91},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 256","100%","img","Dependency tree showing two workspace packages requesting different version ranges of a shared library, producing two nested installs, versus a resolved tree with one hoisted copy","height:auto;max-width:760px;display:block;margin:1.75rem auto;font-family:inherit;color:var(--fp-svg-ink)","\n  ",[94,95],"rect",{"className":96,"x":98,"y":98,"width":88,"height":88,"fill":99},[97],"svg-canvas","0","#ffffff",[101,102,103],"title",{},"Why the same package is installed twice",[105,106,107],"desc",{},"Two workspace packages request incompatible ranges of a shared dependency, so the package manager nests a second copy; aligning the ranges lets both resolve to one hoisted install.",[94,109],{"x":110,"y":110,"width":111,"height":112,"rx":113,"fill":114,"stroke":115,"style":116},"1","758","254","10","none","currentColor","stroke-opacity:0.18",[118,119,123],"text",{"x":120,"y":121,"fill":115,"style":122},"24","32","font-size:16px;font-weight:700","Two ranges, two installs",[118,125,130],{"x":126,"y":127,"fill":115,"style":128,"textAnchor":129},"180","60","font-size:11.5px;font-weight:700;","middle","Before",[94,132],{"x":133,"y":134,"width":135,"height":136,"rx":137,"fill":115,"stroke":115,"style":138},"70","72","220","26","4","fill-opacity:0.05;stroke-opacity:0.3",[118,140,143],{"x":126,"y":141,"fill":115,"style":142,"textAnchor":129},"90","font-size:10.5px;","root workspace",[94,145],{"x":146,"y":147,"width":148,"height":136,"rx":137,"fill":149,"stroke":149,"style":150},"40","112","130","#0466c8","fill-opacity:0.16;stroke-opacity:0.5",[118,152,154],{"x":153,"y":148,"fill":115,"style":142,"textAnchor":129},"105","app  wants ^2.3",[94,156],{"x":157,"y":147,"width":148,"height":136,"rx":137,"fill":149,"stroke":149,"style":150},"190",[118,159,161],{"x":160,"y":148,"fill":115,"style":142,"textAnchor":129},"255","ui  wants ^1.9",[94,163],{"x":146,"y":164,"width":148,"height":136,"rx":137,"fill":165,"stroke":166,"style":167},"152","#ffc300","#b8860b","fill-opacity:0.3",[118,169,171],{"x":153,"y":170,"fill":115,"style":142,"textAnchor":129},"170","lib 2.3.1  (44KB)",[94,173],{"x":157,"y":164,"width":148,"height":136,"rx":137,"fill":165,"stroke":166,"style":167},[118,175,176],{"x":160,"y":170,"fill":115,"style":142,"textAnchor":129},"lib 1.9.4  (41KB)",[118,178,180],{"x":126,"y":179,"fill":166,"style":128,"textAnchor":129},"200","85KB shipped, one library",[182,183],"line",{"x1":184,"y1":185,"x2":184,"y2":135,"stroke":115,"style":186},"380","50","stroke-opacity:0.2",[118,188,190],{"x":189,"y":127,"fill":115,"style":128,"textAnchor":129},"570","After aligning ranges",[94,192],{"x":193,"y":134,"width":135,"height":136,"rx":137,"fill":115,"stroke":115,"style":138},"460",[118,195,143],{"x":189,"y":141,"fill":115,"style":142,"textAnchor":129},[94,197],{"x":198,"y":147,"width":148,"height":136,"rx":137,"fill":149,"stroke":149,"style":150},"430",[118,200,154],{"x":201,"y":148,"fill":115,"style":142,"textAnchor":129},"495",[94,203],{"x":204,"y":147,"width":148,"height":136,"rx":137,"fill":149,"stroke":149,"style":150},"580",[118,206,208],{"x":207,"y":148,"fill":115,"style":142,"textAnchor":129},"645","ui  wants ^2.3",[94,210],{"x":211,"y":164,"width":148,"height":136,"rx":137,"fill":149,"stroke":149,"style":212},"505","fill-opacity:0.24;stroke-opacity:0.55",[118,214,171],{"x":189,"y":170,"fill":115,"style":142,"textAnchor":129},[118,216,217],{"x":189,"y":179,"fill":115,"style":128,"textAnchor":129},"44KB shipped, one instance",[42,219,221],{"id":220},"root-cause-analysis","Root Cause Analysis",[15,223,224,227,228,231,232,235],{},[53,225,226],{},"1. Incompatible semver ranges across workspace packages."," One package pins ",[30,229,230],{},"^1.9"," while another requires ",[30,233,234],{},"^2.3",". The package manager cannot satisfy both with one install, so it nests a second copy — correct behaviour, expensive outcome.",[15,237,238,241],{},[53,239,240],{},"2. A dependency that should have been a peer."," A shared library declared as a regular dependency inside an internal package gets its own copy per consumer. Declaring it as a peer dependency instead makes the consumer supply the single instance.",[15,243,244,247,248,252],{},[53,245,246],{},"3. Mixed module formats."," The same package resolved once as ESM and once as CommonJS produces two distinct modules in the graph even at the same version — the mechanism behind ",[19,249,251],{"href":250},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002F","dual package hazard",".",[15,254,255,258,259,262],{},[53,256,257],{},"4. Aliases and symlinks that bypass resolution."," A build alias pointing at a source path while another import resolves through ",[30,260,261],{},"node_modules"," yields two module identities for what is, on disk, the same code.",[42,264,266],{"id":265},"step-by-step-resolution","Step-by-Step Resolution",[15,268,269],{},[53,270,271],{},"1. Get the authoritative list of duplicates from the package manager.",[273,274,279],"pre",{"className":275,"code":276,"language":277,"meta":278,"style":278},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","# Every package installed at more than one version, with who asked for what.\nnpm ls --all --json 2>\u002Fdev\u002Fnull \\\n  | jq -r '[paths(objects) as $p | {name: ($p[-1]), v: (getpath($p).version)}]\n           | map(select(.v)) | group_by(.name) | map(select((map(.v)|unique|length) > 1))\n           | .[][0].name' | sort -u\n# trade-off: this lists what is INSTALLED, which is a superset of what is\n# BUNDLED — dev-only duplicates are noise here. Cross-check against the bundle\n# report before acting on any entry.\n","bash","",[30,280,281,289,317,332,338,353,359,365],{"__ignoreMap":278},[282,283,285],"span",{"class":182,"line":284},1,[282,286,288],{"class":287},"sjfSM","# Every package installed at more than one version, with who asked for what.\n",[282,290,292,296,300,304,307,311,314],{"class":182,"line":291},2,[282,293,295],{"class":294},"sQw3B","npm",[282,297,299],{"class":298},"sZ8jY"," ls",[282,301,303],{"class":302},"sPXB4"," --all",[282,305,306],{"class":302}," --json",[282,308,310],{"class":309},"sPARh"," 2>",[282,312,313],{"class":298},"\u002Fdev\u002Fnull",[282,315,316],{"class":309}," \\\n",[282,318,320,323,326,329],{"class":182,"line":319},3,[282,321,322],{"class":309},"  |",[282,324,325],{"class":294}," jq",[282,327,328],{"class":302}," -r",[282,330,331],{"class":298}," '[paths(objects) as $p | {name: ($p[-1]), v: (getpath($p).version)}]\n",[282,333,335],{"class":182,"line":334},4,[282,336,337],{"class":298},"           | map(select(.v)) | group_by(.name) | map(select((map(.v)|unique|length) > 1))\n",[282,339,341,344,347,350],{"class":182,"line":340},5,[282,342,343],{"class":298},"           | .[][0].name'",[282,345,346],{"class":309}," |",[282,348,349],{"class":294}," sort",[282,351,352],{"class":302}," -u\n",[282,354,356],{"class":182,"line":355},6,[282,357,358],{"class":287},"# trade-off: this lists what is INSTALLED, which is a superset of what is\n",[282,360,362],{"class":182,"line":361},7,[282,363,364],{"class":287},"# BUNDLED — dev-only duplicates are noise here. Cross-check against the bundle\n",[282,366,368],{"class":182,"line":367},8,[282,369,370],{"class":287},"# report before acting on any entry.\n",[15,372,373],{},"Expected outcome: a short list of genuinely duplicated packages with their requesters.",[15,375,376,379],{},[53,377,378],{},"2. Align the ranges at the source."," The durable fix is agreeing on one range across the workspace, not overriding it downstream.",[273,381,385],{"className":382,"code":383,"language":384,"meta":278,"style":278},"language-json shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","{\n  \"pnpm\": { \"overrides\": { \"date-fns\": \"^3.6.0\" } },\n  \"resolutions\": { \"date-fns\": \"^3.6.0\" }\n}\n","json",[30,386,387,393,419,435],{"__ignoreMap":278},[282,388,389],{"class":182,"line":284},[282,390,392],{"class":391},"saISM","{\n",[282,394,395,399,402,405,407,410,413,416],{"class":182,"line":291},[282,396,398],{"class":397},"sZBmE","  \"pnpm\"",[282,400,401],{"class":391},": { ",[282,403,404],{"class":397},"\"overrides\"",[282,406,401],{"class":391},[282,408,409],{"class":397},"\"date-fns\"",[282,411,412],{"class":391},": ",[282,414,415],{"class":298},"\"^3.6.0\"",[282,417,418],{"class":391}," } },\n",[282,420,421,424,426,428,430,432],{"class":182,"line":319},[282,422,423],{"class":397},"  \"resolutions\"",[282,425,401],{"class":391},[282,427,409],{"class":397},[282,429,412],{"class":391},[282,431,415],{"class":298},[282,433,434],{"class":391}," }\n",[282,436,437],{"class":182,"line":334},[282,438,439],{"class":391},"}\n",[273,441,443],{"className":275,"code":442,"language":277,"meta":278,"style":278},"# Then align the packages that were asking for the old range.\npnpm -r update date-fns@^3.6.0\n# trade-off: an override forces a version a package did not declare support for.\n# It usually works and occasionally breaks at runtime in a way no type check\n# catches — run the affected packages' test suites before shipping it.\n",[30,444,445,450,463,468,473],{"__ignoreMap":278},[282,446,447],{"class":182,"line":284},[282,448,449],{"class":287},"# Then align the packages that were asking for the old range.\n",[282,451,452,455,457,460],{"class":182,"line":291},[282,453,454],{"class":294},"pnpm",[282,456,328],{"class":302},[282,458,459],{"class":298}," update",[282,461,462],{"class":298}," date-fns@^3.6.0\n",[282,464,465],{"class":182,"line":319},[282,466,467],{"class":287},"# trade-off: an override forces a version a package did not declare support for.\n",[282,469,470],{"class":182,"line":334},[282,471,472],{"class":287},"# It usually works and occasionally breaks at runtime in a way no type check\n",[282,474,475],{"class":182,"line":340},[282,476,477],{"class":287},"# catches — run the affected packages' test suites before shipping it.\n",[15,479,480],{},"Expected outcome: one install, one copy in the bundle, and the byte saving equal to the smaller copies.",[15,482,483,486,487,252],{},[53,484,485],{},"3. Make shared libraries peer dependencies."," Inside internal packages, anything that must be a singleton belongs in ",[30,488,489],{},"peerDependencies",[273,491,493],{"className":382,"code":492,"language":384,"meta":278,"style":278},"{\n  \"name\": \"@internal\u002Fui\",\n  \"peerDependencies\": { \"react\": \"^18 || ^19\", \"@internal\u002Fstore\": \"workspace:*\" },\n  \"devDependencies\": { \"react\": \"^19.0.0\" }\n}\n",[30,494,495,499,512,541,557],{"__ignoreMap":278},[282,496,497],{"class":182,"line":284},[282,498,392],{"class":391},[282,500,501,504,506,509],{"class":182,"line":291},[282,502,503],{"class":397},"  \"name\"",[282,505,412],{"class":391},[282,507,508],{"class":298},"\"@internal\u002Fui\"",[282,510,511],{"class":391},",\n",[282,513,514,517,519,522,524,527,530,533,535,538],{"class":182,"line":319},[282,515,516],{"class":397},"  \"peerDependencies\"",[282,518,401],{"class":391},[282,520,521],{"class":397},"\"react\"",[282,523,412],{"class":391},[282,525,526],{"class":298},"\"^18 || ^19\"",[282,528,529],{"class":391},", ",[282,531,532],{"class":397},"\"@internal\u002Fstore\"",[282,534,412],{"class":391},[282,536,537],{"class":298},"\"workspace:*\"",[282,539,540],{"class":391}," },\n",[282,542,543,546,548,550,552,555],{"class":182,"line":334},[282,544,545],{"class":397},"  \"devDependencies\"",[282,547,401],{"class":391},[282,549,521],{"class":397},[282,551,412],{"class":391},[282,553,554],{"class":298},"\"^19.0.0\"",[282,556,434],{"class":391},[282,558,559],{"class":182,"line":340},[282,560,439],{"class":391},[15,562,563],{},"Expected outcome: consumers supply the single instance; the internal package can never bundle its own.",[15,565,566,569],{},[53,567,568],{},"4. Enforce single instances in the bundler as a backstop."," Resolution aliases catch anything the dependency graph still splits.",[273,571,575],{"className":572,"code":573,"language":574,"meta":278,"style":278},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F webpack: one physical path per singleton, whatever the graph says.\nconst path = require('node:path');\nmodule.exports = {\n  resolve: {\n    alias: {\n      react: path.resolve(__dirname, 'node_modules\u002Freact'),\n      'react-dom': path.resolve(__dirname, 'node_modules\u002Freact-dom')\n    },\n    dedupe: ['@internal\u002Fstore']         \u002F\u002F Vite exposes the same idea directly\n  }\n};\n\u002F\u002F trade-off: an alias silently forces one version on code that expected\n\u002F\u002F another. It is a backstop for singletons, not a substitute for fixing the\n\u002F\u002F ranges — and it hides the real problem from the next person who looks.\n","javascript",[30,576,577,582,606,621,626,631,648,666,671,686,692,698,704,710],{"__ignoreMap":278},[282,578,579],{"class":182,"line":284},[282,580,581],{"class":287},"\u002F\u002F webpack: one physical path per singleton, whatever the graph says.\n",[282,583,584,587,590,593,597,600,603],{"class":182,"line":291},[282,585,586],{"class":309},"const",[282,588,589],{"class":302}," path",[282,591,592],{"class":309}," =",[282,594,596],{"class":595},"smZ65"," require",[282,598,599],{"class":391},"(",[282,601,602],{"class":298},"'node:path'",[282,604,605],{"class":391},");\n",[282,607,608,611,613,616,618],{"class":182,"line":319},[282,609,610],{"class":302},"module",[282,612,252],{"class":391},[282,614,615],{"class":302},"exports",[282,617,592],{"class":309},[282,619,620],{"class":391}," {\n",[282,622,623],{"class":182,"line":334},[282,624,625],{"class":391},"  resolve: {\n",[282,627,628],{"class":182,"line":340},[282,629,630],{"class":391},"    alias: {\n",[282,632,633,636,639,642,645],{"class":182,"line":355},[282,634,635],{"class":391},"      react: path.",[282,637,638],{"class":595},"resolve",[282,640,641],{"class":391},"(__dirname, ",[282,643,644],{"class":298},"'node_modules\u002Freact'",[282,646,647],{"class":391},"),\n",[282,649,650,653,656,658,660,663],{"class":182,"line":361},[282,651,652],{"class":298},"      'react-dom'",[282,654,655],{"class":391},": path.",[282,657,638],{"class":595},[282,659,641],{"class":391},[282,661,662],{"class":298},"'node_modules\u002Freact-dom'",[282,664,665],{"class":391},")\n",[282,667,668],{"class":182,"line":367},[282,669,670],{"class":391},"    },\n",[282,672,674,677,680,683],{"class":182,"line":673},9,[282,675,676],{"class":391},"    dedupe: [",[282,678,679],{"class":298},"'@internal\u002Fstore'",[282,681,682],{"class":391},"]         ",[282,684,685],{"class":287},"\u002F\u002F Vite exposes the same idea directly\n",[282,687,689],{"class":182,"line":688},10,[282,690,691],{"class":391},"  }\n",[282,693,695],{"class":182,"line":694},11,[282,696,697],{"class":391},"};\n",[282,699,701],{"class":182,"line":700},12,[282,702,703],{"class":287},"\u002F\u002F trade-off: an alias silently forces one version on code that expected\n",[282,705,707],{"class":182,"line":706},13,[282,708,709],{"class":287},"\u002F\u002F another. It is a backstop for singletons, not a substitute for fixing the\n",[282,711,713],{"class":182,"line":712},14,[282,714,715],{"class":287},"\u002F\u002F ranges — and it hides the real problem from the next person who looks.\n",[42,717,719],{"id":718},"verification","Verification",[15,721,722],{},"Re-run the analyzer and confirm each previously duplicated package appears exactly once. Compare the vendor chunk's size before and after; the saving should match the sum of the removed copies, and if it does not, one copy is still being pulled through a different path.",[15,724,725],{},"For singletons, verify identity at runtime rather than trusting the graph:",[273,727,729],{"className":572,"code":728,"language":574,"meta":278,"style":278},"\u002F\u002F Two copies produce two different module identities.\nimport * as store from '@internal\u002Fstore';\nwindow.__storeInstances ??= new Set();\nwindow.__storeInstances.add(store);\nconsole.assert(window.__storeInstances.size === 1, 'duplicate store instance!');\n",[30,730,731,736,759,776,787],{"__ignoreMap":278},[282,732,733],{"class":182,"line":284},[282,734,735],{"class":287},"\u002F\u002F Two copies produce two different module identities.\n",[282,737,738,741,744,747,750,753,756],{"class":182,"line":291},[282,739,740],{"class":309},"import",[282,742,743],{"class":302}," *",[282,745,746],{"class":309}," as",[282,748,749],{"class":391}," store ",[282,751,752],{"class":309},"from",[282,754,755],{"class":298}," '@internal\u002Fstore'",[282,757,758],{"class":391},";\n",[282,760,761,764,767,770,773],{"class":182,"line":319},[282,762,763],{"class":391},"window.__storeInstances ",[282,765,766],{"class":309},"??=",[282,768,769],{"class":309}," new",[282,771,772],{"class":595}," Set",[282,774,775],{"class":391},"();\n",[282,777,778,781,784],{"class":182,"line":334},[282,779,780],{"class":391},"window.__storeInstances.",[282,782,783],{"class":595},"add",[282,785,786],{"class":391},"(store);\n",[282,788,789,792,795,798,801,804,806,809],{"class":182,"line":340},[282,790,791],{"class":391},"console.",[282,793,794],{"class":595},"assert",[282,796,797],{"class":391},"(window.__storeInstances.size ",[282,799,800],{"class":309},"===",[282,802,803],{"class":302}," 1",[282,805,529],{"class":391},[282,807,808],{"class":298},"'duplicate store instance!'",[282,810,605],{"class":391},[15,812,813,814,252],{},"Then keep it from returning. Duplicates reappear whenever someone adds a dependency, so add a CI check that fails the build when a package resolves to more than one version, and pair it with the bundle-size budget described in ",[19,815,817],{"href":816},"\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",[42,819,821],{"id":820},"faq","FAQ",[823,824,827,831],"details",{"className":825},[826],"faq-item",[828,829,830],"summary",{},"Are duplicate packages always worth fixing?",[15,832,833],{},"Not always. A 3KB utility duplicated across two rarely loaded chunks is noise. Two copies of a state library, a router, or React are worth fixing immediately, because the cost is correctness rather than bytes: hooks throw, context reads as undefined, and two stores drift apart. Sort by size and by singleton-ness, and fix the second category regardless of size.",[823,835,837,840],{"className":836},[826],[828,838,839],{},"Does pnpm prevent duplication automatically?",[15,841,842,843,847],{},"It prevents ",[844,845,846],"em",{},"accidental"," duplication from hoisting, because its strict layout means a package can only import what it declares. It cannot resolve genuinely incompatible ranges — if two workspace packages require different major versions, you get two installs under any package manager. The fix is always to align the ranges or declare a peer dependency.",[823,849,851,854],{"className":850},[826],[828,852,853],{},"Why does the analyzer show two copies when npm ls shows one?",[15,855,856,857,860,861,864,865,868,869,872],{},"Usually a module-format split: the same version resolved once through the ESM entry and once through the CommonJS entry, producing two module identities from one install. It can also be an alias or symlink that gives the bundler two paths to the same file. Check the analyzer's module paths — if they differ only by ",[30,858,859],{},"esm","\u002F",[30,862,863],{},"cjs"," or by a ",[30,866,867],{},"src"," versus ",[30,870,871],{},"dist"," segment, that is your answer.",[823,874,876,879],{"className":875},[826],[828,877,878],{},"How do I stop duplicates from coming back?",[15,880,881],{},"Make the check part of the build rather than a periodic clean-up. A CI step that lists packages resolved at more than one version and fails on any entry in an allowlist of singletons catches the regression on the pull request that introduces it, when the person who added the dependency is still looking at it. Pair it with a size budget on the vendor chunk so a duplicate that slips past the name check is still caught by its bytes.",[823,883,885,888],{"className":884},[826],[828,886,887],{},"Is a duplicate always visible in the analyzer treemap?",[15,889,890],{},"Not reliably. The treemap groups by output chunk, so two copies split across a vendor chunk and a route chunk can look like unrelated blocks. Sorting the module list by package name is a better view, and the package manager's resolution tree is better still because it shows intent — who asked for which range — rather than just the outcome.",[823,892,894,897],{"className":893},[826],[828,895,896],{},"Do overrides break semver guarantees?",[15,898,899],{},"They set them aside deliberately, which is why they are a fix of last resort rather than a first move. Forcing a package to run against a version it never declared support for usually works and occasionally fails at runtime in ways no type check catches. Prefer updating the packages that request the old range; use an override when one of them is a third-party dependency you cannot change, and run its test suite before shipping.",[42,901,903],{"id":902},"related","Related",[47,905,906,911,917],{},[50,907,908,910],{},[19,909,22],{"href":21}," — reading the report that surfaces these duplicates.",[50,912,913,916],{},[19,914,915],{"href":816},"Reducing vendor chunk size in a React app"," — what to do with the chunk once duplicates are gone.",[50,918,919,922],{},[19,920,921],{"href":250},"Fixing dual package hazard in a library"," — the module-format cause of same-version duplication.",[924,925,927],"script",{"type":926},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Find and remove duplicate dependencies in a monorepo bundle\",\n  \"description\": \"Identify packages installed at multiple versions, align the version ranges across workspace packages, convert shared libraries to peer dependencies, and enforce single instances in the bundler.\",\n  \"step\": [\n    { \"@type\": \"HowToStep\", \"position\": 1, \"name\": \"List installed duplicates\", \"text\": \"Use the package manager's resolution tree to find packages present at more than one version and who requested each.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002F#step-by-step-resolution\" },\n    { \"@type\": \"HowToStep\", \"position\": 2, \"name\": \"Align the ranges\", \"text\": \"Agree one version range across the workspace and update the packages that requested the old one.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002F#step-by-step-resolution\" },\n    { \"@type\": \"HowToStep\", \"position\": 3, \"name\": \"Use peer dependencies for singletons\", \"text\": \"Declare shared libraries as peer dependencies in internal packages so consumers supply the single instance.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002F#step-by-step-resolution\" },\n    { \"@type\": \"HowToStep\", \"position\": 4, \"name\": \"Enforce in the bundler\", \"text\": \"Alias singleton packages to one physical path as a backstop and assert single identity at runtime.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002F#verification\" }\n  ]\n}\n",[924,929,930],{"type":926},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Find Duplicate Dependencies in a Monorepo Bundle\",\n  \"description\": \"Duplicates in a workspace build come from incompatible ranges, missing peer declarations and module-format splits. Detect the cause and fix resolution rather than symptoms.\",\n  \"datePublished\": \"2026-08-01\",\n  \"dateModified\": \"2026-08-01\",\n  \"author\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"publisher\": { \"@type\": \"Organization\", \"name\": \"frontend-performance.com\" },\n  \"mainEntityOfPage\": { \"@type\": \"WebPage\", \"@id\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002F\" }\n}\n",[924,932,933],{"type":926},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\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\": \"Finding Duplicate Dependencies in a Monorepo Bundle\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002F\" }\n  ]\n}\n",[935,936,937],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}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 .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--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 .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}",{"title":278,"searchDepth":291,"depth":291,"links":939},[940,941,942,943,944,945],{"id":44,"depth":291,"text":45},{"id":220,"depth":291,"text":221},{"id":265,"depth":291,"text":266},{"id":718,"depth":291,"text":719},{"id":820,"depth":291,"text":821},{"id":902,"depth":291,"text":903},"Trace why the same package appears more than once in a workspace build, and fix the resolution — not the symptom.","md",{"slug":949,"type":950,"breadcrumb":951,"datePublished":958,"dateModified":958},"finding-duplicate-dependencies-in-a-monorepo-bundle","article",[952,954,955,956],{"name":953,"url":860},"Home",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":957},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002F","2026-08-01",true,"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle",{"title":5,"description":962},"Three copies of the same library in one bundle is a resolution problem, not a bundler bug. Detect duplicates, trace why they were hoisted apart, and dedupe safely.","javascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002Findex","oTMmwSQ2lfrH3KW7xdMWjg4Wgvwx6GdSLfUebFiONpU",[966,969],{"title":22,"path":967,"stem":968,"children":-1},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques","javascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Findex",{"title":970,"path":971,"stem":972,"children":-1},"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",1785622407356]