Build a distributable Vite library without bundling your dependencies
Use Vite library mode and Rollup externals to ship a component library that consumers can tree-shake, without shipping React or Vue inside your bundle.
16 Dec 2025, 17:35 UTC

The problem: your library build is shipping React
You have a React component library and you run npx vite build to publish it. Vite finishes successfully, but the output folder contains an index.html, and your JavaScript bundle includes React itself. When a consumer installs your package, their application loads two copies of React: one from your library and one from their own node_modules. The result is a larger bundle, broken hooks, and confusing version mismatches.
This happens because Vite's default vite build is optimized for single-page applications. It assumes an HTML entry, generates client-side routing fallbacks, and bundles dependencies so the app can run standalone. A distributable library needs different behavior.
The takeaway
Use Vite library mode (build.lib) to tell Vite you are building a package for other developers, not a website. Pair it with build.rollupOptions.external so dependencies such as React or Vue are left out of the bundle and provided by the consumer. If your consumers use modern bundlers, you can also skip the UMD format and ship ES modules only.
Configure library mode
Library mode is configured in vite.config.ts. The build.lib.entry option points to your library's main source file. Vite uses Rollup under the hood to bundle that file and writes output to dist by default.
build.lib.name sets the global variable name for UMD builds. It is required only when you include the umd format. build.lib.formats defaults to ['es', 'umd']; setting it to ['es'] produces a modern ES-module bundle that most bundlers can tree-shake.
Dependencies should be listed in build.rollupOptions.external. Marking a dependency external means Rollup leaves import React from 'react' untouched. The consumer's application resolves React from its own node_modules. This prevents duplicate copies, but it also means the consumer must install React themselves. Version mismatches between your library's peer dependency and the consumer's installed copy can still cause issues.
Worked example
Suppose your project looks like this:
src/
index.ts # library entry: exports Button, Input
vite.config.ts
package.json
Your vite.config.ts could look like this:
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import { resolve } from 'node:path';
export default defineConfig({
plugins: [react()],
build: {
lib: {
entry: resolve(__dirname, 'src/index.ts'),
name: 'MyComponentLibrary',
formats: ['es'],
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
},
},
},
});
Run the build from your project root:
npx vite build
No special permissions are required. Vite writes to dist/ by default. Check that dist contains ES-module files such as dist/index.js and dist/assets/, and that no index.html is generated. Library mode intentionally skips HTML generation and SPA fallback, so it is unsuitable for building a standalone website.
To verify the externalization worked, open dist/index.js and confirm it contains an import statement for React rather than React's source code. Then, in a separate consumer project, install your package and React, import your components, and confirm the application starts without duplicate React copies.
Trade-offs and limitations
- UMD globals. If you include the UMD format,
build.lib.nameis required. UMD exposes a global variable that can conflict with other libraries in the host application. - Plugin assumptions. Some Vite plugins assume an HTML entry and may behave differently or fail in library mode.
- External dependencies. Marking React or Vue external shifts installation responsibility to the consumer. If your library requires a specific React version, declare it as a peer dependency in
package.json.
Verify before publishing
- Check your installed Vite version with
npx vite --versionand review release notes for your target version before relying onbuild.libbehavior. - Run
npx vite buildand inspectdist/for the expected ES-module output. - Import the generated ES module in a separate project and verify unused exports are tree-shaken.
- Confirm your external dependencies appear as imports in the bundle, not as bundled source code.
If something goes wrong, reverting is straightforward: remove dist/ and return vite.config.ts to the default application build configuration, then run npx vite build again.
Library mode is the right tool when you are publishing a package for other developers to install. For a customer-facing site, stick with Vite's default application build.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.