This repo demonstrates two setups for library packaging (shared) and how it impacts the bundle size of the consumer (package) :
non-optimized— duplicated dependencies, larger bundleoptimized— shared peerDependencies, smaller bundle
├── optimized/
│ └── packages/
│ ├── shared/
│ └── package/
├── non-optimized/
│ └── packages/
│ ├── shared/
│ └── package
From root of each project (optimized or non-optimized):
npm installnpm run build- Both packages depend directly on
react 18 - No use of
externalin Rollup - Bundle size increases due to duplicated React
{
"name": "shared",
"version": "1.0.0",
"main": "dist/shared.mjs",
"type": "module",
"scripts": {
"build": "rollup -c"
},
"dependencies": {
"react-dom": "^19.0.0",
"react": "^19.0.0"
},
"devDependencies": {}
}import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
export default {
input: "src/index.js",
output: [
{ file: "dist/shared.cjs", format: "cjs" },
{ file: "dist/shared.mjs", format: "es" },
],
plugins: [resolve(), commonjs()],
external: [],
};sharedlistsreact 18as a peerDependencyreact 18is marked as external in Rollup- Final consumer (
package) providesreact 18 - Bundle is smaller and no duplication
{
"name": "shared",
"version": "1.0.0",
"main": "dist/shared.mjs",
"type": "module",
"scripts": {
"build": "rollup -c"
},
"peerDependencies": {
"react": "^19.0.0"
},
"dependencies": {
"react-dom": "^19.0.0",
"react": "^19.0.0"
},
"devDependencies": {}
import resolve from "@rollup/plugin-node-resolve";
import commonjs from "@rollup/plugin-commonjs";
export default {
input: "src/index.js",
output: [
{ file: "dist/shared.cjs", format: "cjs" },
{ file: "dist/shared.mjs", format: "es" },
],
plugins: [resolve(), commonjs()],
external: ["react"],
};| Project | package Size |
|---|---|
| Non-Optimized | 28.85KB |
| Optimized | 9.02KB 🔻 |
Use vite-bundle-visualizer under package and sharedfor bundle breakdowns on both projects:
npx vite-bundle-visualizerYou can also directly open the **-visualization.html files
Explanation :
sharedwill bundle its own copy of React 18 because it's independencies.packagewill bundle its own copy of React 18 as well (from itsnode_modules).- Even though they use the same version semantically, each package treats it as isolated.
Explanation:
- When bundling
shared, the bundler:- Does not include React in the bundle.
- Instead, it assumes React will be present in the consumer environment.
- The
package(consumer app) already has React, so it satisfies the peer dependency ofshared. - Only one copy of React is included — the one from
package. sharedandpackagenow share the same React instance.
Every dependency you pull into a shared library must be reviewed:
- Use
peerDependenciesfor shared packages like React - Always set
externalfor peer deps in Rollup - Avoid re-bundling large libs like React

