Publishing an npm package with Vite library mode
Use Vite build.lib to bundle a library entry point, externalize dependencies, and wire up package.json exports for npm consumers.
04 Oct 2025, 18:38 UTC

The problem: your library ships with React inside
You have a component library built with Vite. You run vite build, publish to npm, and a consumer installs it. Their bundler reports duplicate React copies, or the package points to a development HTML file instead of JavaScript. Vite's default build is optimized for applications, not publishable libraries.
Vite's library mode (build.lib) changes the output from an application bundle to one or more library formats, such as ESM and UMD. It uses Rollup under the hood and lets you mark dependencies as external so they are not bundled into your package.
Configure a single entry point and output formats
Create vite.config.js in your project root. The build.lib.entry value is the file consumers should import first, usually src/index.ts. build.lib.name sets the global variable name for UMD builds. build.lib.formats chooses which formats to emit.
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
build: {
lib: {
entry: './src/index.ts',
name: 'MyComponentLibrary',
formats: ['es', 'umd'],
},
rollupOptions: {
external: ['react', 'react-dom'],
output: {
globals: {
react: 'React',
'react-dom': 'ReactDOM',
},
},
},
},
});
Run this from the project root:
npx vite build
No special permissions are required. Vite writes to dist by default. After the build, check that dist contains files such as index.es.js and index.umd.js, derived from your entry filename and build.lib.name.
Risk: if your application build and library build share the same outDir, the library build can overwrite application assets. Separate them with build.outDir or a build script that targets different directories.
Tell consumers how to import the package
Vite does not read your peerDependencies or write package.json for you. You must add the external packages manually so consumers install them.
{
"name": "my-component-library",
"version": "1.0.0",
"main": "dist/index.umd.js",
"module": "dist/index.es.js",
"exports": {
".": {
"import": "./dist/index.es.js",
"require": "./dist/index.umd.js"
}
},
"peerDependencies": {
"react": ">=18.0.0",
"react-dom": ">=18.0.0"
},
"devDependencies": {
"vite": "^5.0.0"
}
}
Placeholders: replace my-component-library with your published package name, and adjust React versions to match your supported range.
Verify the build in a consumer context
For ESM consumers, install the package in a test app and import it:
import { Button } from 'my-component-library';
Confirm the bundler resolves dist/index.es.js and does not include React in the app bundle.
For UMD, load dist/index.umd.js in a browser and check that the global defined by build.lib.name is available, for example window.MyComponentLibrary.
Trade-offs and limitations
build.lib.nameis required for UMD. Omitting it causes a build error for that format.- Vite does not infer
peerDependenciesfromexternal. Missing peer dependencies cause runtime failures for consumers. - CSS imported by the entry point is extracted to a separate asset, but how consumers consume those styles depends on their bundler setup.
- Tree-shaking depends on static imports in your entry point and the consumer's bundler. Side-effectful dependencies can limit dead-code elimination.
Practical check before publishing
- Run
npx vite buildand listdist. - Confirm
package.jsonexportspoints to the generated files. - Confirm every package in
rollupOptions.externalappears inpeerDependencies. - Install the package in a separate test app and import it.
Assumes Vite 5.x or later; build.lib behavior is stable in recent releases. Library mode is a supported path for publishing Vite-based packages, but it is not automatic. Treat vite.config.js and package.json as a pair: the config builds the files, and the manifest tells consumers where to find them.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.