Building a JavaScript Library for Distribution: Rollup vs Vite Library Mode

Vite library mode abstracts Rollup-compatible bundling through Rolldown to provide automatic ESM/CJS/UMD outputs, built-in CSS extraction, and optimized asset handling, whereas pure Rollup requires manual plugin configuration for each build step.

When building a JavaScript library for distribution in the vitejs/vite ecosystem, developers face a choice between configuring a pure Rollup pipeline or leveraging Vite's opinionated library mode. Both approaches generate the module formats required for modern npm packages, but Vite adds a layer of performance optimizations and sensible defaults that significantly reduce configuration boilerplate. This analysis examines the architectural differences, build processes, and output characteristics to help you optimize for modern bundler practices.

Core Architectural Differences

Bundler Engine and Performance

Rollup (stand-alone) runs on Node.js with single-threaded execution or limited parallelism. Vite library mode uses Rolldown, a Rust-based bundler that maintains Rollup compatibility while leveraging multi-threaded scanning and cache-aware incremental builds. According to the source code in packages/vite/src/node/build.ts, Vite integrates Rolldown at the core of its build pipeline, resulting in faster cold starts and rebuilds compared to traditional Rollup configurations.

Configuration Surface and Defaults

Pure Rollup requires an explicit rollup.config.js where you manually define the input mapping, output array for each format, and plugin chain. In contrast, Vite’s vite.config.ts exposes a build.lib option that merges sensible defaults automatically. When build.lib is enabled, Vite generates ESM, CommonJS, and UMD outputs without requiring separate output configurations, as implemented in the LibraryOptions interface within build.ts.

CSS and Asset Handling

Rollup requires external plugins like rollup-plugin-postcss to extract CSS into a distributable file. Vite handles CSS bundling natively: when build.lib is active, the pipeline automatically emits a single CSS file (dist/<name>.css) and allows customization via the build.lib.cssFileName option. For assets, Vite inlines files smaller than 4 KB by default and hashes larger assets into the assetsDir, whereas Rollup requires manual url plugin configuration for similar behavior.

Environment Variable Replacement

Vite statically replaces all import.meta.env.* references during production builds, leaving process.env.* dynamic for library consumers to define at runtime. This differs from Rollup, where developers must manually install and configure @rollup/plugin-replace to achieve equivalent environment variable substitution.

How Vite Implements Library Mode

When the build.lib flag is set in packages/vite/src/node/build.ts, Vite’s internal build pipeline executes the following steps:

  1. Merges user rolldownOptions with internal defaults — the rolldownOptions property (which replaces the historic rollupOptions alias) allows fine-tuning while preserving Vite’s conventions.
  2. Configures three output targets — automatically generates es, cjs, and umd formats with sensible filenames (.js, .cjs, .umd.cjs).
  3. Bundles CSS imports — collects all CSS dependencies into a single stylesheet using the cssFileName option specified in LibraryOptions.
  4. Generates package.json exports — writes a minimal conditional exports map compatible with modern Node.js and bundler resolution.
  5. Strips HMR plugins — removes module preload polyfills and hot-module replacement code that do not apply to library builds.

This implementation leverages esbuild-based transforms, Rolldown’s parallel graph processing, and Vite’s internal caching layer to optimize the build graph without additional setup.

Practical Configuration Examples

Vite Library Mode Setup

// vite.config.ts
import { defineConfig } from 'vite'

export default defineConfig({
  build: {
    lib: {
      entry: 'src/index.ts',
      name: 'MyLib',
      fileName: (format) => `my-lib.${format}.js`,
      cssFileName: 'my-lib.css',
    },
    rolldownOptions: {
      external: ['vue', 'lodash'],
    },
    minify: 'oxc',
    sourcemap: true,
    assetsInlineLimit: 4096,
  },
})

This configuration emits dist/my-lib.es.js, dist/my-lib.cjs, dist/my-lib.umd.cjs, and dist/my-lib.css (if CSS is imported), with assets under 4 KB inlined automatically.

Equivalent Rollup Configuration

// rollup.config.js
import typescript from '@rollup/plugin-typescript'
import postcss from 'rollup-plugin-postcss'
import { terser } from 'rollup-plugin-terser'

export default {
  input: 'src/index.ts',
  external: ['vue', 'lodash'],
  plugins: [
    typescript(),
    postcss({ extract: true, minimize: true }),
    terser(),
  ],
  output: [
    { file: 'dist/my-lib.es.js', format: 'es', sourcemap: true },
    { file: 'dist/my-lib.cjs', format: 'cjs', sourcemap: true },
    { file: 'dist/my-lib.umd.js', format: 'umd', name: 'MyLib', sourcemap: true },
  ],
}

This manual approach requires explicit plugin installation for TypeScript, CSS extraction, and minification, with no built-in handling for import.meta.env or automatic asset hashing.

Direct Rolldown Configuration

// rolldown.config.ts
import { defineConfig } from 'rolldown'

export default defineConfig({
  input: 'src/index.ts',
  external: ['vue'],
  output: [
    { file: 'dist/my-lib.es.js', format: 'esm' },
    { file: 'dist/my-lib.cjs', format: 'cjs' },
    { file: 'dist/my-lib.umd.js', format: 'umd', name: 'MyLib' },
  ],
})

Using Rolldown directly mirrors Vite’s internal behavior but removes Vite-specific conventions like automatic CSS handling and environment replacement, suitable when you need maximum control over the bundler graph.

When to Prefer Pure Rollup Over Vite

Despite Vite’s optimizations, a pure Rollup configuration remains preferable in specific scenarios:

  • Monorepo consistency: When your organization already maintains complex Rollup configurations across multiple packages, introducing Vite adds an additional abstraction layer.
  • Plugin compatibility: If you rely on Rollup plugins that have not been verified against Rolldown’s specific hook timing or behavior.
  • Custom hook logic: When you need absolute control over every Rollup lifecycle hook, such as bespoke output.assetFileNames functions or custom code-splitting algorithms that override Vite’s defaults.

Summary

  • Vite library mode uses Rolldown (Rust-based, Rollup-compatible) for faster builds compared to Node.js-based Rollup.
  • Automatic output generation produces ESM, CJS, and UMD formats without manual output array configuration.
  • Built-in CSS extraction bundles stylesheets automatically via the cssFileName option, eliminating the need for rollup-plugin-postcss.
  • Asset optimization inlines files under 4 KB and hashes larger assets by default.
  • Environment handling statically replaces import.meta.env.* while preserving process.env.* for consumer-defined variables.
  • Configuration escape hatch via rolldownOptions allows dropping down to raw Rollup-compatible settings when Vite’s opinions conflict with specific requirements.

Frequently Asked Questions

Is Vite library mode faster than pure Rollup for building libraries?

Yes. Vite leverages Rolldown, a Rust-based bundler compatible with the Rollup plugin API, which executes graph processing and module bundling in parallel. According to the implementation in packages/vite/src/node/build.ts and the optimizer integration in rolldownDepPlugin.ts, this architecture provides significantly faster cold starts and incremental rebuilds compared to single-threaded Rollup executions.

How does CSS handling differ between Vite and Rollup when bundling a library?

Vite automatically extracts all imported CSS into a single file (defaulting to dist/<name>.css) when build.lib is enabled, configurable via the cssFileName option in LibraryOptions. Rollup requires manual integration of rollup-plugin-postcss or similar plugins to achieve CSS extraction, with separate configuration needed for minification and output naming.

Can I use existing Rollup plugins with Vite library mode?

Yes. Vite supports the vast majority of Rollup plugins through the rolldownOptions.plugins array (formerly rollupOptions.plugins). However, plugins that rely on specific Node.js-only APIs or untested hook behaviors may require verification against Rolldown’s implementation, as noted in the Vite source code configuration handling.

When should I choose pure Rollup over Vite for library distribution?

Choose pure Rollup when you require bundler independence from the Vite toolchain (such as in monorepos already standardized on Rollup), need unverified Rollup plugins that may conflict with Rolldown, or demand absolute control over every bundler hook and output option. For most modern libraries, Vite’s library mode provides equivalent output guarantees with less configuration overhead.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →