Skip to content

vite-plugin-external

npm package

Excludes listed modules both at runtime and in production bundles. Target scope: Vite 8.x (current rewrite, built on top of the new Rolldown bundler). For Vite 1–6 users see the legacy docs archive.

NPM versionNPM DownloadsNode version

Overview

How it works in Vite 8 + Rolldown

This version is a full rewrite targeting Vite 8 and its built-in Rolldown bundler. Regardless of phase (dev serve / build), every user-facing external shape (object map / function / string / RegExp / array / true) is first normalised to a single decision-hook signature (see Options Reference → ExternalFn type), so the three entry points share the exact same decision logic — no more phase drift:

  1. Dev phase — DepsOptimizer pre-bundling: a custom Rolldown plugin is injected so that "named" externals (e.g. react → React, which carry a global name or CDN URL) resolve to on-disk stash files the plugin writes. Pure externals (string / regex / true / function returning true) are simply forwarded to Rolldown's native external flag.
  2. Dev phase — browser request: resolves proceed through Vite's normal middleware layer.
  3. Build phase: all externals are wired into build.rolldownOptions.external; for ES-format CDN externals a <link rel="modulepreload"> is injected via transformIndexHtml so the browser starts prefetching the CDN modules on first paint.

The old Vite ≤6 dual-path design (alias for dev + rollupOptions for build + interop: 'auto' compatibility switch + rollback: true escape hatch) has been archived. If you need the historical implementation please consult the Legacy archive (Vite 1–6) → vite-plugin-external.

Installation

bash
npm add vite-plugin-external
bash
pnpm add vite-plugin-external
bash
yarn add vite-plugin-external

IIFE build (global variable injected via an HTML <script> tag)

js
import { defineConfig } from 'vite';
import pluginExternal from 'vite-plugin-external';

export default defineConfig({
  plugins: [
    pluginExternal({
      externals: {
        jquery: '$',
        vue: 'Vue',
        react: 'React',
        'react-dom/client': 'ReactDOM',
      },
    }),
  ],
  build: {
    rolldownOptions: {
      output: {
        format: 'iife',
      },
    },
  },
});

Function-style externals (dynamic global name resolution)

js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import pluginExternal from 'vite-plugin-external';

export default defineConfig({
  plugins: [
    react({ jsxRuntime: 'classic' }),
    pluginExternal({
      externals(libName) {
        if (libName === 'react') return 'React';
        if (libName === 'react-dom/client') return 'ReactDOM';
      },
    }),
  ],
  build: {
    rolldownOptions: {
      output: {
        format: 'iife',
      },
    },
  },
});

ESM build (re-export from an absolute CDN ESM URL)

js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
import pluginExternal from 'vite-plugin-external';

export default defineConfig({
  plugins: [
    react({ jsxRuntime: 'classic' }),
    pluginExternal({
      externals: {
        react: 'https://esm.sh/react@18.3.1',
        'react-dom/client': 'https://esm.sh/react-dom@18.3.1',
      },
    }),
  ],
});

FAQ

  • Q: After editing externals during dev the page no longer loads?
  • A: Named externals stash files are cached under ./node_modules/.vite_external. Delete that folder to force a cache rebuild (it lives next to Vite's own .vite cache, so a one-liner rm -rf node_modules/.vite* cleans both).

Historical changelog

  • 8.0.3

    • Support for two configuration attributes 'nodeBuiltins' and 'externalizeDeps' to be overridden multiple times in the specified mode
  • 8.0.2

    • Added "type": "module" to package.json
  • 8.0.1

    • Removed deprecated rollback option, updated docs
  • 8.0.0

    • This Vite-8-compatible rewrite is based on the v6.2.0 codebase but reworks every pipeline for Rolldown + the new DepsOptimizer.