Skip to main content

Rollup vs Vite 2026: When You Need a Dedicated Bundler

·PkgPulse Team
0

TL;DR

Vite for apps, Rollup for libraries — and Vite uses Rollup internally anyway. Vite (~18M weekly downloads) is the default for web applications in 2026. Rollup (~15M downloads) remains the best choice for building JavaScript/TypeScript libraries that will be published to npm. The key distinction: Vite wraps Rollup with a development server, HMR, and framework plugins that you simply do not need when building a library destined for npm. For app development, reach for Vite. For library authoring with precise output control, reach for Rollup.

Key Takeaways

  • Vite uses Rollup for production builds — they share the same output quality
  • Rollup produces the cleanest library bundles — best tree-shaking in the ecosystem
  • Vite is better for applications — dev server, HMR, plugin ecosystem for frameworks
  • Vite also has a library mode (build.lib) — but bare Rollup gives more output control
  • Rollup plugin ecosystem is fully available in Vite (backward compatible)
  • tsup is the modern shortcut — wraps esbuild but produces Rollup-quality output for 90% of library use cases

The Relationship Between Rollup and Vite

Vite is not a competitor to Rollup. It is built on top of Rollup:

Vite architecture:
  ┌─────────────────────────────────────┐
  │ Vite                                │
  │  ├── Dev server (native ESM)        │
  │  ├── esbuild (transpile + dep prep) │
  │  └── Rollup (production builds) ←──┼── This IS Rollup
  └─────────────────────────────────────┘

When you run vite build, you are running Rollup with Vite's plugin transform layer on top. The output quality is identical to running Rollup directly. This means choosing between them is not about output quality — it is about what else you need around the build step.

For an application, you want Vite because you get hot module replacement during development, CSS handling, image optimization, and a plugin ecosystem tuned for React, Vue, Svelte, and other frameworks.

For a library, you want Rollup (or a Rollup-based tool) because you only care about the production artifact. There is no dev server, no browser, no HMR — just an input file and a set of output formats.


Why Use Rollup Directly for Library Authoring

When you publish a package to npm, your consumers will use Vite, webpack, or some other bundler to consume your package. What matters to them:

  1. An ESM output (dist/index.js) for tree-shaking
  2. A CJS output (dist/index.cjs) for Node.js compatibility
  3. A UMD output if you need CDN usage via <script> tags
  4. Type declarations (dist/index.d.ts)
  5. sideEffects: false in package.json for aggressive tree-shaking

Rollup handles all of these with full control:

// rollup.config.js — for a npm library
import typescript from '@rollup/plugin-typescript';
import resolve from '@rollup/plugin-node-resolve';
import commonjs from '@rollup/plugin-commonjs';
import { dts } from 'rollup-plugin-dts';

export default [
  // Build the JavaScript
  {
    input: 'src/index.ts',
    output: [
      {
        file: 'dist/index.cjs',
        format: 'cjs',          // CommonJS for Node.js
        exports: 'named',
      },
      {
        file: 'dist/index.js',
        format: 'esm',          // ES modules for modern bundlers
        exports: 'named',
        sourcemap: true,
      },
      {
        file: 'dist/index.umd.js',
        format: 'umd',          // UMD for CDN / <script> usage
        name: 'MyLibrary',
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM',
        },
      },
    ],
    plugins: [
      resolve(),
      commonjs(),
      typescript({ tsconfig: './tsconfig.build.json' }),
    ],
    external: ['react', 'react-dom'], // Never bundle peer deps
  },
  // Build the type definitions
  {
    input: 'src/index.ts',
    output: { file: 'dist/index.d.ts', format: 'esm' },
    plugins: [dts()],
  },
];

This produces exactly:

  • dist/index.js — ES module for Vite/webpack tree-shaking
  • dist/index.cjs — CommonJS for older Node.js
  • dist/index.umd.js — UMD for CDN usage
  • dist/index.d.ts — TypeScript types

Pair this with a proper package.json exports field:

{
  "exports": {
    ".": {
      "import": "./dist/index.js",
      "require": "./dist/index.cjs",
      "types": "./dist/index.d.ts"
    }
  },
  "sideEffects": false
}

Vite's Library Mode

Vite does have a library mode via build.lib that handles many of the same cases:

// vite.config.ts — library mode
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import dts from 'vite-plugin-dts';

export default defineConfig({
  plugins: [
    react(),
    dts({ insertTypesEntry: true }),
  ],
  build: {
    lib: {
      entry: 'src/index.ts',
      name: 'MyLibrary',
      formats: ['es', 'cjs', 'umd'],
      fileName: (format) => `index.${format}.js`,
    },
    rollupOptions: {
      external: ['react', 'react-dom'],
      output: {
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM',
        },
      },
    },
  },
});

So when should you choose bare Rollup over Vite's library mode? The main scenarios:

  • Complex multi-entry libraries — Rollup handles multiple entry points and chunk splitting with more precision
  • Custom code transformations — Rollup's plugin API is more granular for AST-level transforms
  • Avoiding framework dependencies — Vite's library mode still pulls in Vite's own dependencies; bare Rollup keeps your build toolchain lean
  • Fine-grained external control — Rollup's external option accepts functions, RegExp patterns, and per-output-format configuration

Bundle Analysis

Both ecosystems have visualization tools to understand what is in your bundle:

# Rollup: rollup-plugin-visualizer
npm install --save-dev rollup-plugin-visualizer
// rollup.config.js
import { visualizer } from 'rollup-plugin-visualizer';

export default {
  plugins: [
    visualizer({
      open: true,        // Opens browser automatically
      filename: 'stats.html',
      gzipSize: true,
      brotliSize: true,
    }),
  ],
};
# Vite: rollup-plugin-visualizer works here too
npm install --save-dev rollup-plugin-visualizer
// vite.config.ts
import { visualizer } from 'rollup-plugin-visualizer';

export default defineConfig({
  plugins: [
    visualizer({
      open: true,
      filename: 'dist/stats.html',
    }),
  ],
});

Because Vite uses Rollup internally, the same rollup-plugin-visualizer works in both. There is also vite-bundle-visualizer (a thin wrapper) and rollup-plugin-bundle-stats for CI integration.


Tree-Shaking

Rollup pioneered tree-shaking in the JavaScript bundler ecosystem. Its tree-shaking is still considered the gold standard:

// Your library exports three things
export function add(a: number, b: number) { return a + b; }
export function multiply(a: number, b: number) { return a * b; }
export const VERSION = '1.0.0';

// Consumer imports only add
import { add } from 'your-library';

// Rollup output — multiply and VERSION are completely eliminated
// webpack output — depends on sideEffects field; can include unreferenced exports
// esbuild output — similar quality to Rollup for simple cases

For libraries where consumer bundle size matters, Rollup's tree-shaking is worth using directly or through tsup. The difference is most visible with larger libraries where eliminating unused exports measurably reduces the consumer's bundle.


tsup: The Modern Library Build Tool

For most library authors in 2026, tsup handles the 90% case without requiring a hand-crafted Rollup config:

// tsup.config.ts — replaces custom Rollup config for most libraries
import { defineConfig } from 'tsup';

export default defineConfig({
  entry: ['src/index.ts'],
  format: ['cjs', 'esm'],       // Both output formats
  dts: true,                     // Generate .d.ts files
  splitting: false,
  sourcemap: true,
  clean: true,
  minify: false,                 // Libraries usually skip minification
  external: ['react'],           // Peer dependencies
  treeshake: true,
});

tsup uses esbuild internally (not Rollup), but produces comparable output. The tradeoff: faster build times, slightly less control. For complex multi-output libraries or those needing Rollup-specific plugins, bare Rollup is still the better tool.


Package Health

PackageWeekly DownloadsSize (gzip)Latest VersionActive
rollup~15M~1.5MB install4.xYes
vite~18M~4MB install6.xYes
tsup~5M~2MB install8.xYes
esbuild~30M~10MB install0.24.xYes

Both Rollup and Vite are actively maintained. Vite is backed by the Vue team and has the higher download count due to application usage. Rollup is the more foundational tool and sees steady growth as library authoring continues to grow. Neither is at risk of abandonment.


Configuration Complexity

TaskRollupVite lib modetsup
Single output formatMediumLowMinimal
ESM + CJS + UMD + typesHighMediumMinimal
Framework appNot idealLowNot needed
Multi-entry libraryHighMediumLow
Custom AST transformsHigh (plugins)Medium (plugins)Low

When to Choose Each

Use Vite when:

  • Building a web application (React, Vue, Svelte, vanilla)
  • You need a dev server with HMR during development
  • You want framework-specific plugins (React Fast Refresh, Vue SFC, Svelte HMR)
  • You are setting up a new project and want sensible defaults

Use Rollup directly when:

  • Building a library with unusual multi-format output requirements
  • You need precise control over what gets bundled and what stays external
  • You are writing custom build plugins that need deep Rollup API access
  • You need UMD output with specific global variable names for CDN usage

Use tsup when:

  • Building a TypeScript npm library (the modern default for most cases)
  • You want Rollup-quality tree-shaking without the configuration overhead
  • You are building a CLI tool or Node.js package

The 2026 JavaScript Stack Cheatsheet

One PDF: the best package for every category (ORMs, bundlers, auth, testing, state management). Used by 500+ devs. Free, updated monthly.