[{"data":1,"prerenderedAt":936},["ShallowReactive",2],{"content:\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library":3,"surroundings:\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library":927},{"id":4,"title":5,"body":6,"description":908,"extension":909,"meta":910,"navigation":921,"path":922,"seo":923,"stem":925,"__hash__":926},"content\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002Findex.md","Fixing Dual Package Hazard in a Library",{"type":7,"value":8,"toc":900},"minimark",[9,14,34,37,42,83,229,233,252,269,280,286,290,295,419,425,565,595,598,604,610,711,715,718,721,724,787,791,803,812,825,834,843,860,864,885,890,893,896],[10,11,13],"h1",{"id":12},"how-to-fix-dual-package-hazard-in-a-library","How to Fix Dual Package Hazard in a Library",[15,16,17,18,23,24,28,29,33],"p",{},"This walkthrough extends ",[19,20,22],"a",{"href":21},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002F","Modern Module Formats: ESM vs CommonJS"," inside ",[19,25,27],{"href":26},"\u002Fjavascript-bundle-optimization-code-splitting\u002F","JavaScript Bundle Optimization & Code Splitting",". The symptom is bizarre until you know the cause: a context provider set at the top of the app reads as ",[30,31,32],"code",{},"undefined"," in a component, or a registry populated at startup is empty at use, or the analyzer shows one library counted twice at the same version.",[15,35,36],{},"Both halves of your application are correct. They are simply talking to two different copies of the same module — one loaded through the package's ESM entry, one through its CommonJS entry.",[38,39,41],"h2",{"id":40},"rapid-diagnosis-prove-there-are-two-instances","Rapid Diagnosis: Prove There Are Two Instances",[43,44,45,53,67,77],"ul",{},[46,47,48,52],"li",{},[49,50,51],"strong",{},"Add an identity check."," Instantiate a module-level marker and assert there is only one; two proves the duplication immediately.",[46,54,55,58,59,62,63,66],{},[49,56,57],{},"Look for the same version twice in the bundle report."," Paths ending in ",[30,60,61],{},"dist\u002Fesm\u002F..."," and ",[30,64,65],{},"dist\u002Fcjs\u002F..."," for the same package are the giveaway.",[46,68,69,72,73,76],{},[49,70,71],{},"Check which consumers import which way."," A dependency that still uses ",[30,74,75],{},"require()"," will take the CommonJS branch while your app takes the ESM one.",[46,78,79,82],{},[49,80,81],{},"Test the failing behaviour in isolation."," If a context works when both consumer and provider live in the same file, the problem is module identity, not your logic.",[84,85,92,93,100,101,100,105,100,109,100,118,100,125,100,134,100,141,100,144,100,148,100,155,100,160,100,163,100,166,100,173,100,177,100,179,100,182,100,187,100,192,100,195,100,199,100,201,100,204,100,209,100,212,100,215,100,221,100,226,100],"svg",{"xmlns":86,"viewBox":87,"width":88,"role":89,"ariaLabel":90,"style":91},"http:\u002F\u002Fwww.w3.org\u002F2000\u002Fsvg","0 0 760 250","100%","img","An app importing a library through the ESM entry while a dependency requires the CommonJS entry, producing two module instances with separate state, versus a single shared instance after fixing the exports map","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"," ",[102,103,104],"title",{},"Two entry points, two module instances",[106,107,108],"desc",{},"The application resolves the library through its ESM entry while a transitive dependency resolves the CommonJS entry, creating two separate instances with independent state; a single resolved entry restores one shared instance.",[94,110],{"x":111,"y":111,"width":112,"height":113,"rx":114,"fill":115,"stroke":116,"style":117},"1","758","248","10","none","currentColor","stroke-opacity:0.18",[119,120,124],"text",{"x":121,"y":122,"fill":116,"style":123},"24","32","font-size:16px;font-weight:700","One package, two identities",[94,126],{"x":127,"y":128,"width":129,"height":130,"rx":131,"fill":132,"stroke":132,"style":133},"40","66","130","28","5","#0466c8","fill-opacity:0.2;stroke-opacity:0.55",[119,135,140],{"x":136,"y":137,"fill":116,"style":138,"textAnchor":139},"105","85","font-size:10.5px;","middle","your app (import)",[94,142],{"x":127,"y":143,"width":129,"height":130,"rx":131,"fill":132,"stroke":132,"style":133},"122",[119,145,147],{"x":136,"y":146,"fill":116,"style":138,"textAnchor":139},"141","dep (require)",[149,150],"line",{"x1":151,"y1":152,"x2":153,"y2":152,"stroke":116,"style":154},"170","80","212","stroke-opacity:0.45",[156,157],"polygon",{"points":158,"fill":116,"style":159},"220,80 210,75 210,85","fill-opacity:0.55",[149,161],{"x1":151,"y1":162,"x2":153,"y2":162,"stroke":116,"style":154},"136",[156,164],{"points":165,"fill":116,"style":159},"220,136 210,131 210,141",[94,167],{"x":168,"y":128,"width":169,"height":130,"rx":131,"fill":170,"stroke":171,"style":172},"220","150","#ffc300","#b8860b","fill-opacity:0.3",[119,174,176],{"x":175,"y":137,"fill":116,"style":138,"textAnchor":139},"295","dist\u002Fesm\u002Findex.js",[94,178],{"x":168,"y":143,"width":169,"height":130,"rx":131,"fill":170,"stroke":171,"style":172},[119,180,181],{"x":175,"y":146,"fill":116,"style":138,"textAnchor":139},"dist\u002Fcjs\u002Findex.js",[119,183,186],{"x":175,"y":184,"fill":171,"style":185,"textAnchor":139},"176","font-size:11.5px;font-weight:700;","two stores, two contexts",[149,188],{"x1":189,"y1":190,"x2":189,"y2":168,"stroke":116,"style":191},"420","50","stroke-opacity:0.2",[94,193],{"x":194,"y":128,"width":129,"height":130,"rx":131,"fill":132,"stroke":132,"style":133},"450",[119,196,198],{"x":197,"y":137,"fill":116,"style":138,"textAnchor":139},"515","your app",[94,200],{"x":194,"y":143,"width":129,"height":130,"rx":131,"fill":132,"stroke":132,"style":133},[119,202,203],{"x":197,"y":146,"fill":116,"style":138,"textAnchor":139},"dep",[149,205],{"x1":206,"y1":152,"x2":207,"y2":208,"stroke":116,"style":154},"580","614","100",[149,210],{"x1":206,"y1":162,"x2":207,"y2":211,"stroke":116,"style":154},"112",[156,213],{"points":214,"fill":116,"style":159},"622,106 610,101 611,111",[94,216],{"x":217,"y":218,"width":219,"height":130,"rx":131,"fill":132,"stroke":132,"style":220},"622","92","106","fill-opacity:0.24;stroke-opacity:0.55",[119,222,225],{"x":223,"y":224,"fill":116,"style":138,"textAnchor":139},"675","111","one instance",[119,227,228],{"x":223,"y":184,"fill":116,"style":185,"textAnchor":139},"shared state restored",[38,230,232],{"id":231},"root-cause-analysis","Root Cause Analysis",[15,234,235,100,238,241,242,245,246,248,249,251],{},[49,236,237],{},"1. Two entry fields resolved by different consumers.",[30,239,240],{},"main"," points at CommonJS, ",[30,243,244],{},"module"," at ESM. A bundler following ",[30,247,244],{}," and a dependency calling ",[30,250,75],{}," end up in different files, and module identity is per file.",[15,253,254,261,262,62,265,268],{},[49,255,256,257,260],{},"2. An ",[30,258,259],{},"exports"," map with inconsistent conditions."," If ",[30,263,264],{},"import",[30,266,267],{},"require"," resolve to different builds of the same source, any mixed graph loads both. This is correct for Node interop and hazardous for anything holding state.",[15,270,271,274,275,279],{},[49,272,273],{},"3. Module-level state in the library."," The hazard only ",[276,277,278],"em",{},"hurts"," when the library owns something singular: a context, a registry, a store, an event bus, a WeakMap keyed on identity. A pure utility library duplicates bytes and nothing else.",[15,281,282,285],{},[49,283,284],{},"4. A transitive dependency stuck on CommonJS."," You may have converted your own imports, but one dependency still requires the package, which is enough to pull the second copy in.",[38,287,289],{"id":288},"step-by-step-resolution","Step-by-Step Resolution",[15,291,292],{},[49,293,294],{},"1. Prove the duplication before changing packaging.",[296,297,302],"pre",{"className":298,"code":299,"language":300,"meta":301,"style":301},"language-javascript shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","\u002F\u002F In the library itself, during development.\nconst INSTANCE = Symbol.for('@acme\u002Fstore.instance');\nif (globalThis[INSTANCE]) {\n  console.error('[@acme\u002Fstore] duplicate instance detected — dual package hazard');\n}\nglobalThis[INSTANCE] = true;\n\u002F\u002F trade-off: a Symbol.for registry key is global and permanent, so it is a\n\u002F\u002F diagnostic, not a fix. Keep it behind a development-only flag so production\n\u002F\u002F bundles do not carry it.\n","javascript","",[30,303,304,312,344,359,375,381,401,407,413],{"__ignoreMap":301},[305,306,308],"span",{"class":149,"line":307},1,[305,309,311],{"class":310},"sjfSM","\u002F\u002F In the library itself, during development.\n",[305,313,315,319,323,326,330,334,337,341],{"class":149,"line":314},2,[305,316,318],{"class":317},"sPARh","const",[305,320,322],{"class":321},"sPXB4"," INSTANCE",[305,324,325],{"class":317}," =",[305,327,329],{"class":328},"saISM"," Symbol.",[305,331,333],{"class":332},"smZ65","for",[305,335,336],{"class":328},"(",[305,338,340],{"class":339},"sZ8jY","'@acme\u002Fstore.instance'",[305,342,343],{"class":328},");\n",[305,345,347,350,353,356],{"class":149,"line":346},3,[305,348,349],{"class":317},"if",[305,351,352],{"class":328}," (globalThis[",[305,354,355],{"class":321},"INSTANCE",[305,357,358],{"class":328},"]) {\n",[305,360,362,365,368,370,373],{"class":149,"line":361},4,[305,363,364],{"class":328},"  console.",[305,366,367],{"class":332},"error",[305,369,336],{"class":328},[305,371,372],{"class":339},"'[@acme\u002Fstore] duplicate instance detected — dual package hazard'",[305,374,343],{"class":328},[305,376,378],{"class":149,"line":377},5,[305,379,380],{"class":328},"}\n",[305,382,384,387,389,392,395,398],{"class":149,"line":383},6,[305,385,386],{"class":328},"globalThis[",[305,388,355],{"class":321},[305,390,391],{"class":328},"] ",[305,393,394],{"class":317},"=",[305,396,397],{"class":321}," true",[305,399,400],{"class":328},";\n",[305,402,404],{"class":149,"line":403},7,[305,405,406],{"class":310},"\u002F\u002F trade-off: a Symbol.for registry key is global and permanent, so it is a\n",[305,408,410],{"class":149,"line":409},8,[305,411,412],{"class":310},"\u002F\u002F diagnostic, not a fix. Keep it behind a development-only flag so production\n",[305,414,416],{"class":149,"line":415},9,[305,417,418],{"class":310},"\u002F\u002F bundles do not carry it.\n",[15,420,421,424],{},[49,422,423],{},"2. Give both conditions the same underlying module where state lives."," The safest packaging keeps one implementation and makes the CommonJS entry a thin wrapper around it.",[296,426,430],{"className":427,"code":428,"language":429,"meta":301,"style":301},"language-json shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","{\n  \"name\": \"@acme\u002Fstore\",\n  \"type\": \"module\",\n  \"exports\": {\n    \".\": {\n      \"types\": \".\u002Fdist\u002Findex.d.ts\",\n      \"import\": \".\u002Fdist\u002Findex.js\",\n      \"require\": \".\u002Fdist\u002Findex.cjs\"\n    }\n  },\n  \"main\": \".\u002Fdist\u002Findex.cjs\",\n  \"module\": \".\u002Fdist\u002Findex.js\",\n  \"sideEffects\": false\n}\n","json",[30,431,432,437,452,464,472,479,491,503,513,518,524,537,549,560],{"__ignoreMap":301},[305,433,434],{"class":149,"line":307},[305,435,436],{"class":328},"{\n",[305,438,439,443,446,449],{"class":149,"line":314},[305,440,442],{"class":441},"sZBmE","  \"name\"",[305,444,445],{"class":328},": ",[305,447,448],{"class":339},"\"@acme\u002Fstore\"",[305,450,451],{"class":328},",\n",[305,453,454,457,459,462],{"class":149,"line":346},[305,455,456],{"class":441},"  \"type\"",[305,458,445],{"class":328},[305,460,461],{"class":339},"\"module\"",[305,463,451],{"class":328},[305,465,466,469],{"class":149,"line":361},[305,467,468],{"class":441},"  \"exports\"",[305,470,471],{"class":328},": {\n",[305,473,474,477],{"class":149,"line":377},[305,475,476],{"class":441},"    \".\"",[305,478,471],{"class":328},[305,480,481,484,486,489],{"class":149,"line":383},[305,482,483],{"class":441},"      \"types\"",[305,485,445],{"class":328},[305,487,488],{"class":339},"\".\u002Fdist\u002Findex.d.ts\"",[305,490,451],{"class":328},[305,492,493,496,498,501],{"class":149,"line":403},[305,494,495],{"class":441},"      \"import\"",[305,497,445],{"class":328},[305,499,500],{"class":339},"\".\u002Fdist\u002Findex.js\"",[305,502,451],{"class":328},[305,504,505,508,510],{"class":149,"line":409},[305,506,507],{"class":441},"      \"require\"",[305,509,445],{"class":328},[305,511,512],{"class":339},"\".\u002Fdist\u002Findex.cjs\"\n",[305,514,515],{"class":149,"line":415},[305,516,517],{"class":328},"    }\n",[305,519,521],{"class":149,"line":520},10,[305,522,523],{"class":328},"  },\n",[305,525,527,530,532,535],{"class":149,"line":526},11,[305,528,529],{"class":441},"  \"main\"",[305,531,445],{"class":328},[305,533,534],{"class":339},"\".\u002Fdist\u002Findex.cjs\"",[305,536,451],{"class":328},[305,538,540,543,545,547],{"class":149,"line":539},12,[305,541,542],{"class":441},"  \"module\"",[305,544,445],{"class":328},[305,546,500],{"class":339},[305,548,451],{"class":328},[305,550,552,555,557],{"class":149,"line":551},13,[305,553,554],{"class":441},"  \"sideEffects\"",[305,556,445],{"class":328},[305,558,559],{"class":321},"false\n",[305,561,563],{"class":149,"line":562},14,[305,564,380],{"class":328},[296,566,568],{"className":298,"code":567,"language":300,"meta":301,"style":301},"\u002F\u002F dist\u002Findex.cjs is generated as a re-export shim, not a second implementation.\n\u002F\u002F Its module-level state must come FROM the ESM build, not be redeclared.\n\u002F\u002F trade-off: a shim cannot re-export live bindings to CommonJS consumers\n\u002F\u002F perfectly. For a library whose whole purpose is shared state, shipping ESM\n\u002F\u002F only is cleaner than maintaining a correct interop shim.\n",[30,569,570,575,580,585,590],{"__ignoreMap":301},[305,571,572],{"class":149,"line":307},[305,573,574],{"class":310},"\u002F\u002F dist\u002Findex.cjs is generated as a re-export shim, not a second implementation.\n",[305,576,577],{"class":149,"line":314},[305,578,579],{"class":310},"\u002F\u002F Its module-level state must come FROM the ESM build, not be redeclared.\n",[305,581,582],{"class":149,"line":346},[305,583,584],{"class":310},"\u002F\u002F trade-off: a shim cannot re-export live bindings to CommonJS consumers\n",[305,586,587],{"class":149,"line":361},[305,588,589],{"class":310},"\u002F\u002F perfectly. For a library whose whole purpose is shared state, shipping ESM\n",[305,591,592],{"class":149,"line":377},[305,593,594],{"class":310},"\u002F\u002F only is cleaner than maintaining a correct interop shim.\n",[15,596,597],{},"Expected outcome: state lives in exactly one module regardless of which condition a consumer resolves.",[15,599,600,603],{},[49,601,602],{},"3. Consider shipping ESM only."," For a library with singleton semantics, the dual build is the problem. Every current bundler and Node version supports ESM, and the interop burden moves to the small number of consumers that still need it.",[15,605,606,609],{},[49,607,608],{},"4. Deduplicate at the application level as a stopgap."," If you do not control the library, force one resolution in your build.",[296,611,613],{"className":298,"code":612,"language":300,"meta":301,"style":301},"\u002F\u002F vite: one physical path wins, whichever entry the graph asks for.\nexport default {\n  resolve: {\n    alias: [{ find: \u002F^@acme\\\u002Fstore$\u002F, replacement: '\u002Fnode_modules\u002F@acme\u002Fstore\u002Fdist\u002Findex.js' }],\n    dedupe: ['@acme\u002Fstore']\n  }\n};\n\u002F\u002F trade-off: forcing the ESM build on a consumer that expected CommonJS can\n\u002F\u002F break it at runtime if it relies on require-time semantics. Test the\n\u002F\u002F dependency that was requiring it, not only your own code.\n",[30,614,615,620,632,637,673,684,689,696,701,706],{"__ignoreMap":301},[305,616,617],{"class":149,"line":307},[305,618,619],{"class":310},"\u002F\u002F vite: one physical path wins, whichever entry the graph asks for.\n",[305,621,622,625,628],{"class":149,"line":314},[305,623,624],{"class":317},"export",[305,626,627],{"class":317}," default",[305,629,631],{"class":630},"sQw3B"," {\n",[305,633,634],{"class":149,"line":346},[305,635,636],{"class":328},"  resolve: {\n",[305,638,639,642,645,648,651,655,658,661,664,667,670],{"class":149,"line":361},[305,640,641],{"class":328},"    alias: [{ find:",[305,643,644],{"class":339}," \u002F",[305,646,647],{"class":317},"^",[305,649,650],{"class":339},"@acme",[305,652,654],{"class":653},"sn2CQ","\\\u002F",[305,656,657],{"class":339},"store",[305,659,660],{"class":317},"$",[305,662,663],{"class":339},"\u002F",[305,665,666],{"class":328},", replacement: ",[305,668,669],{"class":339},"'\u002Fnode_modules\u002F@acme\u002Fstore\u002Fdist\u002Findex.js'",[305,671,672],{"class":328}," }],\n",[305,674,675,678,681],{"class":149,"line":377},[305,676,677],{"class":328},"    dedupe: [",[305,679,680],{"class":339},"'@acme\u002Fstore'",[305,682,683],{"class":328},"]\n",[305,685,686],{"class":149,"line":383},[305,687,688],{"class":328},"  }\n",[305,690,691,694],{"class":149,"line":403},[305,692,693],{"class":630},"}",[305,695,400],{"class":328},[305,697,698],{"class":149,"line":409},[305,699,700],{"class":310},"\u002F\u002F trade-off: forcing the ESM build on a consumer that expected CommonJS can\n",[305,702,703],{"class":149,"line":415},[305,704,705],{"class":310},"\u002F\u002F break it at runtime if it relies on require-time semantics. Test the\n",[305,707,708],{"class":149,"line":520},[305,709,710],{"class":310},"\u002F\u002F dependency that was requiring it, not only your own code.\n",[38,712,714],{"id":713},"verification","Verification",[15,716,717],{},"Run the identity check in a production build with the real dependency graph — a development server can resolve differently and hide the problem. One instance is a pass; two means something in the graph still takes the other branch.",[15,719,720],{},"Then confirm in the bundle report that the package appears exactly once, and check the byte saving: removing a duplicate typically recovers the full size of the smaller build.",[15,722,723],{},"Add a guard so a future dependency cannot reintroduce it:",[296,725,729],{"className":726,"code":727,"language":728,"meta":301,"style":301},"language-bash shiki shiki-themes github-light-high-contrast github-dark-high-contrast github-light-high-contrast","# Fail the build if both entry variants of a singleton package are emitted.\nnode -e '\n  const s = require(\".\u002Fdist\u002Fstats.json\");\n  const hits = Object.keys(s.modules ?? {}).filter((m) => m.includes(\"@acme\u002Fstore\"));\n  const variants = new Set(hits.map((m) => (m.includes(\"cjs\") ? \"cjs\" : \"esm\")));\n  if (variants.size > 1) { console.error(\"dual package hazard:\", hits); process.exit(1); }\n'\n# trade-off: this pattern-matches on paths, so a package that names its builds\n# differently slips through. Keep the runtime identity assertion as the real\n# safety net and treat this as an early warning.\n","bash",[30,730,731,736,747,752,757,762,767,772,777,782],{"__ignoreMap":301},[305,732,733],{"class":149,"line":307},[305,734,735],{"class":310},"# Fail the build if both entry variants of a singleton package are emitted.\n",[305,737,738,741,744],{"class":149,"line":314},[305,739,740],{"class":630},"node",[305,742,743],{"class":321}," -e",[305,745,746],{"class":339}," '\n",[305,748,749],{"class":149,"line":346},[305,750,751],{"class":339},"  const s = require(\".\u002Fdist\u002Fstats.json\");\n",[305,753,754],{"class":149,"line":361},[305,755,756],{"class":339},"  const hits = Object.keys(s.modules ?? {}).filter((m) => m.includes(\"@acme\u002Fstore\"));\n",[305,758,759],{"class":149,"line":377},[305,760,761],{"class":339},"  const variants = new Set(hits.map((m) => (m.includes(\"cjs\") ? \"cjs\" : \"esm\")));\n",[305,763,764],{"class":149,"line":383},[305,765,766],{"class":339},"  if (variants.size > 1) { console.error(\"dual package hazard:\", hits); process.exit(1); }\n",[305,768,769],{"class":149,"line":403},[305,770,771],{"class":339},"'\n",[305,773,774],{"class":149,"line":409},[305,775,776],{"class":310},"# trade-off: this pattern-matches on paths, so a package that names its builds\n",[305,778,779],{"class":149,"line":415},[305,780,781],{"class":310},"# differently slips through. Keep the runtime identity assertion as the real\n",[305,783,784],{"class":149,"line":520},[305,785,786],{"class":310},"# safety net and treat this as an early warning.\n",[38,788,790],{"id":789},"faq","FAQ",[792,793,796,800],"details",{"className":794},[795],"faq-item",[797,798,799],"summary",{},"Is dual package hazard always a problem?",[15,801,802],{},"No. For a stateless utility library, two copies cost bytes and nothing else — annoying but not incorrect. It becomes a genuine bug the moment the library owns something singular: a React context, a store, a plugin registry, an event emitter, or a WeakMap keyed on module identity. Those are the packages worth auditing first.",[792,804,806,809],{"className":805},[795],[797,807,808],{},"Should new libraries ship CommonJS at all?",[15,810,811],{},"Increasingly, no. Every maintained bundler and Node release supports ESM, and shipping a single format removes this entire failure class along with half the packaging configuration. Ship CommonJS only when you have identified consumers who genuinely cannot consume ESM, and treat it as a compatibility shim rather than a co-equal build.",[792,813,815,818],{"className":814},[795],[797,816,817],{},"Why does my React context work in dev but not in production?",[15,819,820,821,824],{},"Because the dev server and the production bundler resolve conditions differently — dev servers often prefer the ESM branch uniformly, while the production build follows the full graph including a dependency that requires the CommonJS one. The result is two ",[30,822,823],{},"createContext"," calls in production and one in development, so the provider and the consumer look at different contexts.",[792,826,828,831],{"className":827},[795],[797,829,830],{},"Which packages should I audit first?",[15,832,833],{},"Anything that owns identity or state: React and its renderer, routers, state stores, dependency-injection containers, i18n instances, plugin registries, and any library documented as requiring a single instance. Those are the ones where duplication is a functional bug rather than a byte cost. A quick pass is to search your dependency list for packages whose README mentions \"singleton\", \"provider\" or \"must only be loaded once\" — that phrasing is a reliable marker.",[792,835,837,840],{"className":836},[795],[797,838,839],{},"Do bundler-level dedupe options solve this permanently?",[15,841,842],{},"They solve it for your build, not for your consumers. An alias forces one physical path in the application you control, which is exactly the right stopgap, but a library shipping ambiguous entry points will keep breaking every other consumer in the same way. If you own the package, fix the packaging; if you do not, alias locally and open an issue upstream with the identity assertion as evidence.",[792,844,846,849],{"className":845},[795],[797,847,848],{},"How does this interact with peer dependencies?",[15,850,851,852,855,856,859],{},"They address different halves of the same requirement. A peer dependency ensures there is one ",[276,853,854],{},"installed"," copy by making the consumer supply it; a correct exports map ensures that copy resolves to one ",[276,857,858],{},"module instance"," whichever way it is imported. A package can get the peer declaration right and still duplicate itself through a dual build, so a stateful library needs both.",[38,861,863],{"id":862},"related","Related",[43,865,866,871,878],{},[46,867,868,870],{},[19,869,22],{"href":21}," — how the two systems differ and why interop is hard.",[46,872,873,877],{},[19,874,876],{"href":875},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fwebpack-bundle-analysis-techniques\u002Ffinding-duplicate-dependencies-in-a-monorepo-bundle\u002F","Finding duplicate dependencies in a monorepo bundle"," — the version-range cause of the same symptom.",[46,879,880,884],{},[19,881,883],{"href":882},"\u002Fjavascript-bundle-optimization-code-splitting\u002Ftree-shaking-and-dead-code-elimination\u002F","Tree Shaking and Dead Code Elimination"," — the other place packaging fields change what ships.",[886,887,889],"script",{"type":888},"application\u002Fld+json","\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"HowTo\",\n  \"name\": \"Fix dual package hazard in a library\",\n  \"description\": \"Detect two module instances of one package, align the exports map so stateful modules resolve once, consider shipping ESM only, and deduplicate at the application level as a stopgap.\",\n  \"step\": [\n    { \"@type\": \"HowToStep\", \"position\": 1, \"name\": \"Prove the duplication\", \"text\": \"Add a development-only global identity marker that reports when a second instance loads.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002F#step-by-step-resolution\" },\n    { \"@type\": \"HowToStep\", \"position\": 2, \"name\": \"Align the exports map\", \"text\": \"Make the CommonJS entry a shim over the single implementation so module state is not redeclared.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002F#step-by-step-resolution\" },\n    { \"@type\": \"HowToStep\", \"position\": 3, \"name\": \"Consider ESM only\", \"text\": \"For libraries with singleton semantics, ship one format and remove the interop surface entirely.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002F#step-by-step-resolution\" },\n    { \"@type\": \"HowToStep\", \"position\": 4, \"name\": \"Deduplicate in the app\", \"text\": \"Alias and dedupe the package in the bundler when you do not control the library, then verify one instance in production.\", \"url\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002F#verification\" }\n  ]\n}\n",[886,891,892],{"type":888},"\n{\n  \"@context\": \"https:\u002F\u002Fschema.org\",\n  \"@type\": \"TechArticle\",\n  \"headline\": \"How to Fix Dual Package Hazard in a Library\",\n  \"description\": \"When ESM and CommonJS entries resolve to different files, one package becomes two module instances with separate state. Diagnose and repair the packaging.\",\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\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002F\" }\n}\n",[886,894,895],{"type":888},"\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\": \"Modern Module Formats: ESM vs CommonJS\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002F\" },\n    { \"@type\": \"ListItem\", \"position\": 4, \"name\": \"Fixing Dual Package Hazard in a Library\", \"item\": \"https:\u002F\u002Ffrontend-performance.com\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002F\" }\n  ]\n}\n",[897,898,899],"style",{},"html pre.shiki code .sjfSM, html code.shiki .sjfSM{--shiki-default:#66707B;--shiki-dark:#BDC4CC;--shiki-light:#66707B}html pre.shiki code .sPARh, html code.shiki .sPARh{--shiki-default:#A0111F;--shiki-dark:#FF9492;--shiki-light:#A0111F}html pre.shiki code .sPXB4, html code.shiki .sPXB4{--shiki-default:#023B95;--shiki-dark:#91CBFF;--shiki-light:#023B95}html pre.shiki code .saISM, html code.shiki .saISM{--shiki-default:#0E1116;--shiki-dark:#F0F3F6;--shiki-light:#0E1116}html pre.shiki code .smZ65, html code.shiki .smZ65{--shiki-default:#622CBC;--shiki-dark:#DBB7FF;--shiki-light:#622CBC}html pre.shiki code .sZ8jY, html code.shiki .sZ8jY{--shiki-default:#032563;--shiki-dark:#ADDCFF;--shiki-light:#032563}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 .sZBmE, html code.shiki .sZBmE{--shiki-default:#024C1A;--shiki-dark:#72F088;--shiki-light:#024C1A}html pre.shiki code .sQw3B, html code.shiki .sQw3B{--shiki-default:#702C00;--shiki-dark:#FFB757;--shiki-light:#702C00}html pre.shiki code .sn2CQ, html code.shiki .sn2CQ{--shiki-default:#024C1A;--shiki-default-font-weight:bold;--shiki-dark:#72F088;--shiki-dark-font-weight:bold;--shiki-light:#024C1A;--shiki-light-font-weight:bold}",{"title":301,"searchDepth":314,"depth":314,"links":901},[902,903,904,905,906,907],{"id":40,"depth":314,"text":41},{"id":231,"depth":314,"text":232},{"id":288,"depth":314,"text":289},{"id":713,"depth":314,"text":714},{"id":789,"depth":314,"text":790},{"id":862,"depth":314,"text":863},"When one dependency resolves through both entry points, state stops being shared and the bundle carries both copies. Diagnose and repair the packaging.","md",{"slug":911,"type":912,"breadcrumb":913,"datePublished":920,"dateModified":920},"fixing-dual-package-hazard-in-a-library","article",[914,916,917,918],{"name":915,"url":663},"Home",{"name":27,"url":26},{"name":22,"url":21},{"name":5,"url":919},"\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002F","2026-08-01",true,"\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library",{"title":5,"description":924},"Shipping ESM and CommonJS can load your library twice — two module instances, two singletons, doubled bytes. Fix the exports map, or ship one format.","javascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Ffixing-dual-package-hazard-in-a-library\u002Findex","9CycxfXrNvEakM2BzcGmPTXthCD_2_XXipKUyjkNTnE",[928,932],{"title":929,"path":930,"stem":931},"Deferring Non-Critical Analytics Scripts Safely","\u002Fjavascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Fdeferring-non-critical-analytics-scripts-safely","javascript-bundle-optimization-code-splitting\u002Fmodern-module-formats-esm-vs-commonjs\u002Fdeferring-non-critical-analytics-scripts-safely\u002Findex",{"title":933,"path":934,"stem":935},"Third-Party Script Performance","\u002Fjavascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance","javascript-bundle-optimization-code-splitting\u002Fthird-party-script-performance\u002Findex",1785622407475]