Publishing Reusable Components with Vite Library Mode
Vite Library Mode switches your build from an app bundle to a distributable package. Learn how to configure build.lib, manage peer dependencies, and verify the output.
05 Jul 2026, 16:28 UTC

Why the default Vite build is wrong for libraries
You built a reusable component, but shipping it feels harder than building it. Vite's default build assumes you are deploying a single-page application: it expects an index.html, bundles every reachable import, and optimizes for one specific browser target. A component library needs the opposite—clean named exports, multiple formats, and no HTML shell. Vite Library Mode solves this by switching the build target from an app to a distributable package.
Configuring build.lib
The core setting lives under build.lib in vite.config.js or vite.config.ts. At minimum, you provide an entry path. Optional fields include name (the global variable name for UMD builds), formats (defaults to ['es', 'umd']), and fileName. When this block is present, Vite skips HTML generation and delegates to Rollup with library-oriented defaults. These instructions assume Vite 4 or 5.
Worked example: a Vue 3 button
Assume a project created with npm create vite@latest ui-button -- --template vue (run in your terminal with Node.js installed). Create src/components/Button.vue with a simple button. Then update vite.config.js:
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
build: {
lib: {
entry: 'src/components/Button.vue',
name: 'UiButton',
formats: ['es', 'umd']
}
}
})
Run npm run build from the project root (requires dependencies installed via npm install). Expected check: a dist/ directory appears containing UiButton.es.js, UiButton.umd.js, and UiButton.css. The ES module should export the component by name; the UMD build exposes it on a global when loaded via a script tag. Risk: if peer dependencies are missing, Rollup may inline Vue, inflating the bundle.
Declare peer dependencies and package fields
In package.json, add:
{
"main": "dist/UiButton.umd.js",
"module": "dist/UiButton.es.js",
"peerDependencies": {
"vue": "^3.3.0"
}
}
The main field serves CommonJS/UMD consumers, while module tells modern bundlers to prefer the ES build. Listing Vue under peerDependencies keeps it external so consumers supply their own copy.
Trade-offs you need to plan for
- TypeScript declarations are not automatic. Library Mode does not emit
.d.tsfiles. Use a plugin such asvite-plugin-dtsor a separatetsc --emitDeclarationOnlyrun, then add atypesfield topackage.json. - CSS Modules are limited. Rollup outputs plain CSS files. If your component relies on Vite's CSS Modules class hashing, consumers will not receive the same scoping guarantees without additional tooling.
- No SSR bundle. Library Mode targets browser and universal module consumption. It does not generate a server-side rendering bundle; you need a separate build step if your library is used in SSR frameworks.
- The
nameoption only affects UMD. ES consumers rely entirely on named exports, so ensure your entry file exports components explicitly.
Verify before you publish
- Inspect
dist/after building and confirm the expected files exist. - Run
npm packfrom the project root to create a tarball. Risk: without afilesallowlist or.npmignore, the tarball may include source files andnode_modules. Add"files": ["dist"]topackage.jsonto limit contents. - In a separate Vite app, run
npm install ../ui-button/ui-button-0.0.0.tgzfrom that app's root. Import the component and runnpm run dev. If the button renders and Vue appears only once in the bundle, the external dependency setup works. - For a size check, run
npx source-map-explorer dist/UiButton.es.jsfrom the library root. Vue should appear as an external import, not bundled code.
Library Mode gives you a clean, Rollup-powered path from source to npm package. The build itself is straightforward; the real work is declaring peer dependencies, emitting types, and verifying that consumers receive a lean bundle rather than a bloated one.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.