Jesse A. Green / notebook

Live demo: the DocCompiler guide library as a static, read-only snapshot (October 2026). No backend, no accounts, nothing to submit.

Vite User Guide

Generated May 2026 from vitejs/vite at 66f3194

AI-generated documentation. The library is checked by sampling (three repositories so far); this guide was not individually reviewed. Verify against the project before relying on it.

Vite v8.0.8 — Next-generation frontend build tool powered by native ES modules. Created by Evan You, Vite delivers lightning-fast dev server startup, instant Hot Module Replacement, and optimized production builds via Rolldown.

This guide covers everything you need to install, configure, and use Vite effectively across all supported frameworks and environments.


1. Quick Start

Create a New Project

The fastest way to start is with create-vite, Vite's official scaffolding tool:

npm create vite@latest my-app

This launches an interactive prompt to select a framework and variant. To skip the prompts, pass a template directly:

npm create vite@latest my-app -- --template react-ts

Expected output:

Scaffolding project in /path/to/my-app...

Done. Now run:

  cd my-app
  npm install
  npm run dev

Start Development

cd my-app
npm install
npm run dev

Expected output:

  VITE v8.0.8  ready in 237 ms

  ➜  Local:   http://localhost:5173/
  ➜  Network: use --host to expose
  ➜  press h + enter to show help

Open http://localhost:5173/ in your browser. Edit any source file and watch changes appear instantly.

Build for Production

npm run build

Expected output:

vite v8.0.8 building for production...
✓ 42 modules transformed.
dist/index.html                  0.46 kB │ gzip:  0.30 kB
dist/assets/index-BfH3kQ2f.css   1.42 kB │ gzip:  0.74 kB
dist/assets/index-DiwrgTda.js  143.36 kB │ gzip: 46.10 kB
✓ built in 1.23s

Preview the Build

npm run preview

Expected output:

  ➜  Local:   http://localhost:4173/
  ➜  Network: use --host to expose

Tip

The create-vite CLI is also available as cva. Both binary names work identically: npx cva my-app --template vue-ts.


2. Installation

System Requirements

Requirement Version
Node.js ^20.19.0 or >=22.12.0
Package manager npm, yarn, pnpm, or bun
OS Windows, macOS, Linux

Important

Vite v8 requires Node.js 20.19.0+ or 22.12.0+. Older Node.js versions (16, 18, odd-numbered) are not supported. Check your version with node --version.

Install via Package Manager

npm

npm create vite@latest my-project
cd my-project
npm install

yarn

yarn create vite my-project
cd my-project
yarn

pnpm

pnpm create vite my-project
cd my-project
pnpm install

bun

bun create vite my-project
cd my-project
bun install

Add Vite to an Existing Project

Install Vite as a dev dependency:

npm install -D vite

Create a minimal index.html at the project root:

<!DOCTYPE html>
<html lang="en">
  <head>
    <meta charset="UTF-8" />
    <meta name="viewport" content="width=device-width, initial-scale=1.0" />
    <title>My App</title>
  </head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/main.js"></script>
  </body>
</html>

Add scripts to package.json:

{
  "scripts": {
    "dev": "vite",
    "build": "vite build",
    "preview": "vite preview"
  }
}

Scaffolding Templates

create-vite ships with templates for every major framework:

Framework Templates
Vanilla vanilla, vanilla-ts
Vue vue, vue-ts
React react, react-ts, react-compiler, react-compiler-ts
Preact preact, preact-ts
Lit lit, lit-ts
Svelte svelte, svelte-ts
Solid solid, solid-ts
Qwik qwik, qwik-ts
Ember ember (TypeScript/JavaScript via ember-cli)
Angular angular
Marko marko (via marko run)

Community-maintained variants are also available during interactive scaffolding:

Framework Community variants
Vue custom-create-vue, Nuxt, Vike
React RSC, React Router, TanStack Router, RedwoodSDK, Vike
Svelte SvelteKit
Solid TanStack Router, Vike
Qwik Qwik City
Angular Analog

create-vite CLI Options

Option Description
-t, --template NAME Use a specific template (skip framework prompt)
-i, --immediate Install dependencies and start dev server automatically
--interactive / --no-interactive Force or disable interactive prompts
--overwrite Overwrite existing target directory
-h, --help Display help

Example with all options:

npm create vite@latest my-app -- --template react-ts --immediate --overwrite

This creates the project, installs dependencies, and starts the dev server in one command.


3. Configuration

Config File

Vite reads configuration from vite.config.js (or vite.config.ts) at the project root. Use the defineConfig helper for IntelliSense:

import { defineConfig } from 'vite'

export default defineConfig({
  // options
})

TypeScript config files work out of the box:

import { defineConfig } from 'vite'

export default defineConfig({
  // full type support
})

Essential Options

Base Path

Set the public base path for deployment to a subdirectory:

export default defineConfig({
  base: '/my-app/',
})

Public Directory

Static assets in publicDir are served at root and copied to the build output as-is:

export default defineConfig({
  publicDir: 'public', // default
})

Files in public/ are accessible at /filename — they are never processed by Vite.

Resolve Aliases

Map import paths to directories:

import { defineConfig } from 'vite'
import path from 'node:path'

export default defineConfig({
  resolve: {
    alias: {
      '@': path.resolve(__dirname, './src'),
      '~components': path.resolve(__dirname, './src/components'),
    },
  },
})

Usage in source:

import App from '@/App.vue'
import Button from '~components/Button.jsx'

Plugins

Add plugins as an array:

import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react'

export default defineConfig({
  plugins: [react()],
})

Compile-Time Constants

Replace global constants at build time:

export default defineConfig({
  define: {
    __APP_VERSION__: JSON.stringify('1.2.3'),
    'process.env.NODE_ENV': JSON.stringify('production'),
  },
})

Use in source code:

console.log(`Running version ${__APP_VERSION__}`)

Dev Server Configuration

export default defineConfig({
  server: {
    host: '0.0.0.0',        // listen on all interfaces
    port: 3000,              // custom port
    strictPort: true,        // fail if port is taken
    open: true,              // open browser on start
    cors: true,              // enable CORS
    headers: {               // custom response headers
      'X-Custom-Header': 'value',
    },
    hmr: {
      overlay: true,         // show error overlay (default)
    },
    forwardConsole: {
      enabled: true,         // forward browser console to terminal
    },
    fs: {
      strict: true,          // restrict file serving to workspace
      allow: [
        '/path/to/allowed/dir',
      ],
    },
  },
})

Note

server.fs.strict restricts file access to the workspace root by default. Use server.fs.allow to whitelist additional directories (e.g., monorepo siblings or symlinked packages).

Build Configuration

export default defineConfig({
  build: {
    outDir: 'dist',                // output directory (default)
    assetsInlineLimit: 4096,       // inline assets < 4KB as base64
    sourcemap: true,               // generate source maps
    cssCodeSplit: true,            // CSS code splitting (default)
    manifest: true,                // generate manifest.json
    emptyOutDir: true,             // clear outDir before build
    watch: null,                   // set to {} for watch mode
    ssr: false,                    // SSR build mode
    rollupOptions: {
      // Rolldown options passthrough
      input: 'index.html',
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
        },
      },
    },
  },
})

Library Mode

Build a reusable library instead of an application:

import { defineConfig } from 'vite'
import path from 'node:path'

export default defineConfig({
  build: {
    lib: {
      entry: path.resolve(__dirname, 'src/index.ts'),
      name: 'MyLib',
      fileName: 'my-lib',
    },
    rollupOptions: {
      external: ['react', 'react-dom'],
      output: {
        globals: {
          react: 'React',
          'react-dom': 'ReactDOM',
        },
      },
    },
  },
})

Preview Server Configuration

export default defineConfig({
  preview: {
    port: 4173,
    headers: {
      'Cache-Control': 'public, max-age=600',
    },
  },
})

CSS Configuration

export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/variables" as *;`,
      },
      less: {
        modifyVars: {
          'primary-color': '#1DA57A',
        },
        javascriptEnabled: true,
      },
    },
  },
})

JSON Handling

export default defineConfig({
  json: {
    namedExports: true,   // allow named imports from .json (default)
    stringify: 'auto',    // stringify large JSON for performance (default)
  },
})

TypeScript / JSX Transpilation

Vite v8 uses oxc by default for TypeScript and JSX transpilation. To use esbuild instead:

export default defineConfig({
  oxc: false, // disable oxc, fall back to esbuild
  esbuild: {
    banner: '/* built with esbuild */',
    footer: '/* end */',
  },
})

Conditional Config

Export a function for environment-aware configuration:

export default defineConfig(({ command, mode }) => {
  if (command === 'serve') {
    return {
      // dev-specific config
      server: { port: 3000 },
    }
  } else {
    return {
      // build-specific config
      build: { sourcemap: true },
    }
  }
})

Specifying a Config File

Override the default config file path:

vite --config my-config.js

Tip

For the complete configuration reference including all nested options and their defaults, see the Vite Admin Guide.


4. Core Usage

Dev Server

Start the development server:

vite
# or equivalently:
vite dev
vite serve

The dev server serves your project root as a web server with native ES module support. When you import a module in your source, the browser fetches it directly — no bundling step. This is what makes Vite's startup nearly instant regardless of application size.

Hot Module Replacement (HMR)

Vite provides out-of-the-box HMR for:

  • Vue single-file components (via @vitejs/plugin-vue)
  • React components (via @vitejs/plugin-react or @vitejs/plugin-react-swc)
  • Svelte components (via @sveltejs/vite-plugin-svelte)
  • CSS / PostCSS / Sass / Less / Stylus — all CSS changes apply without full page reload
  • JSX / TSX — with appropriate framework plugins

The HMR error overlay displays compile errors directly in the browser. Disable it with:

export default defineConfig({
  server: {
    hmr: {
      overlay: false,
    },
  },
})

HMR API

For custom HMR handling in modules, use the import.meta.hot API:

if (import.meta.hot) {
  import.meta.hot.accept((newModule) => {
    // handle the updated module
  })

  import.meta.hot.dispose(() => {
    // cleanup side effects
  })
}

import.meta.hot is undefined in production builds and tree-shaken away.

Forward Console

Vite v8 can forward console.log, console.warn, and console.error calls from the browser to your server terminal:

export default defineConfig({
  server: {
    forwardConsole: {
      enabled: true,
    },
  },
})

This is useful for debugging without switching to browser DevTools.

Production Builds

Build your application for production:

vite build

Vite v8 uses Rolldown as the production bundler (replacing Rollup from earlier versions). Rolldown is a Rust-based bundler that provides significantly faster build times while maintaining Rollup plugin API compatibility.

The build produces optimized output in dist/ by default:

  • JavaScript is minified and tree-shaken
  • CSS is extracted, minified, and code-split
  • Assets below assetsInlineLimit (4KB default) are inlined as base64
  • Assets above the limit are hashed and output to dist/assets/

Watch Mode

Rebuild on file changes:

vite build --watch

Or configure in vite.config.js:

export default defineConfig({
  build: {
    watch: {},
  },
})

Build Targets

Specify browser targets:

vite build --target es2022

Preview Server

After building, preview the production output locally:

vite preview

This starts a static file server serving dist/. It is not intended for production use — use a proper web server (nginx, Caddy, etc.) to serve your built files in production.

Dependency Pre-Bundling

Vite automatically pre-bundles dependencies from node_modules on first dev server start. This:

  1. Converts CommonJS and UMD packages to ESM
  2. Consolidates packages with many internal modules into a single request (e.g., lodash-es has 600+ modules)

Pre-bundled dependencies are cached in node_modules/.vite/. Vite re-bundles when:

  • Package manager lockfile changes (package-lock.json, yarn.lock, pnpm-lock.yaml, bun.lockb)
  • Relevant fields in vite.config.js change
  • Patches in patches/ folder change

Force a re-bundle:

vite --force

Or:

vite optimize

Note

The vite optimize command explicitly runs the dependency pre-bundling step. This is primarily useful for debugging — in normal development, Vite handles pre-bundling automatically.


5. Common Tasks

CSS Processing

Plain CSS

Import CSS directly in JavaScript modules:

import './style.css'

Vite handles @import inlining, URL rebasing, and PostCSS automatically (if a PostCSS config is present).

CSS Modules

Any file ending in .module.css is treated as a CSS Module:

/* button.module.css */
.button {
  background: blue;
  color: white;
}

.primary {
  background: darkblue;
}
import styles from './button.module.css'

document.getElementById('btn').className = styles.button
// renders as: class="button_abc123"

Sass / SCSS

Install the preprocessor:

npm install -D sass

Use .scss or .sass files directly:

import './styles/app.scss'

Configure global variables or mixins:

export default defineConfig({
  css: {
    preprocessorOptions: {
      scss: {
        additionalData: `@use "@/styles/variables" as *;`,
      },
    },
  },
})

Less

npm install -D less
import './styles/app.less'

Stylus

npm install -D stylus
import './styles/app.styl'

LightningCSS

Vite supports LightningCSS as an alternative CSS transformer and minifier, providing significantly faster CSS processing.

CSS Code Splitting

In production builds, Vite automatically extracts CSS used by async chunks into separate files. They load in parallel with the chunk's JavaScript — no flash of unstyled content.

Disable CSS code splitting:

export default defineConfig({
  build: {
    cssCodeSplit: false, // bundle all CSS into one file
  },
})

TypeScript

Vite supports TypeScript out of the box. In v8, oxc is the default transpiler (replacing esbuild from earlier versions).

Import .ts and .tsx files directly:

// main.ts
import { createApp } from './app'
createApp('#root')

Important

Vite performs transpilation only — it does not run type checking. Run tsc --noEmit separately (or use vue-tsc for Vue projects) to type-check your code during CI or as a parallel process.

JSX and TSX

JSX is supported in .jsx and .tsx files. Framework-specific JSX transforms require the corresponding plugin:

  • React: @vitejs/plugin-react
  • Vue: @vitejs/plugin-vue-jsx
  • Preact: @preact/preset-vite
  • Solid: vite-plugin-solid

Static Assets

Import static assets to get their resolved URL:

import imgUrl from './image.png'

document.getElementById('hero').src = imgUrl
// In dev: /src/image.png
// In build: /assets/image-a1b2c3d4.png

Supported Asset Types

Vite recognizes these file extensions as static assets:

Category Extensions
Images apng, bmp, png, jpg, jpeg, jfif, pjpeg, pjp, gif, svg, ico, webp, avif, cur, jxl
Media mp4, webm, ogg, mp3, wav, flac, aac, opus, mov, m4a, vtt
Fonts woff, woff2, eot, ttf, otf
Other webmanifest, pdf, txt

Add custom asset types:

export default defineConfig({
  assetsInclude: ['**/*.gltf', '**/*.hdr'],
})

Asset Inline Limit

Assets smaller than assetsInlineLimit (default: 4096 bytes / 4KB) are inlined as base64 data URLs. Larger assets are emitted as files with content hashes.

export default defineConfig({
  build: {
    assetsInlineLimit: 8192, // 8KB
  },
})

Import Suffixes

Vite supports special URL suffixes to control how assets are imported:

Suffix Effect Example
?raw Import as raw string import shader from './frag.glsl?raw'
?url Import as URL (skip inlining) import iconUrl from './icon.svg?url'
?inline Force inline as data URL import img from './large.png?inline'
?no-inline Force file emission (skip inline) import tiny from './dot.png?no-inline'
?worker Import as Web Worker import Worker from './worker.js?worker'
?worker&inline Inline Worker as base64 blob import Worker from './worker.js?worker&inline'
?worker&url Worker as URL import workerUrl from './worker.js?worker&url'
?sharedworker Import as SharedWorker import SW from './sw.js?sharedworker'
?sharedworker&inline Inline SharedWorker import SW from './sw.js?sharedworker&inline'
?sharedworker&url SharedWorker as URL import swUrl from './sw.js?sharedworker&url'
?url&inline URL with inline hint import u from './file.txt?url&inline'
?url&no-inline URL without inline import u from './file.txt?url&no-inline'

Raw Imports

Import file contents as a string:

import vertexShader from './shaders/vertex.glsl?raw'
import license from '../LICENSE?raw'

console.log(vertexShader)
// outputs the raw GLSL source

Web Workers

Import a script as a Web Worker:

import MyWorker from './worker.js?worker'

const worker = new MyWorker()
worker.postMessage({ cmd: 'start' })
worker.onmessage = (e) => {
  console.log('Result:', e.data)
}

Inline the worker (useful when you cannot serve separate files):

import InlineWorker from './worker.js?worker&inline'
const worker = new InlineWorker()

SharedWorker usage:

import SharedWorker from './shared.js?sharedworker'
const shared = new SharedWorker()

WebAssembly

Import .wasm files with the ?init suffix:

import init from './processor.wasm?init'

const instance = await init()
const result = instance.exports.process(42)

Glob Import

Dynamically import multiple modules matching a glob pattern:

const modules = import.meta.glob('./modules/*.js')
// returns:
// {
//   './modules/foo.js': () => import('./modules/foo.js'),
//   './modules/bar.js': () => import('./modules/bar.js'),
// }

// Lazy-load a specific module:
const foo = await modules['./modules/foo.js']()

Eager loading (import all at once):

const modules = import.meta.glob('./modules/*.js', { eager: true })
// All modules are already imported

Named imports for tree-shaking:

const setups = import.meta.glob('./modules/*.js', {
  import: 'setup',
})
// Each module's `setup` export is available as a lazy loader

Import as raw strings:

const templates = import.meta.glob('./templates/*.html', {
  query: '?raw',
  import: 'default',
})

Multiple patterns:

const modules = import.meta.glob([
  './components/**/*.vue',
  './pages/**/*.vue',
])

Negative patterns (exclusions):

const modules = import.meta.glob([
  './components/**/*.vue',
  '!./components/internal/**',
])

Dynamic Import with Variables

Vite supports dynamic imports with template literal variables:

const page = 'home'
const module = await import(`./pages/${page}.js`)

Variables in dynamic imports are limited to one level of depth. Vite analyzes the possible matches at build time and generates code to load them.

Environment Variables

.env Files

Vite loads environment variables from .env files using dotenv and dotenv-expand:

File Purpose Loaded when
.env Base variables Always
.env.local Local overrides (gitignored) Always
.env.development Development variables vite dev
.env.production Production variables vite build
.env.development.local Local dev overrides vite dev
.env.production.local Local prod overrides vite build

Exposing Variables to Client Code

Only variables prefixed with VITE_ are exposed to client code:

# .env
DB_PASSWORD=secret           # NOT exposed to client
VITE_API_URL=https://api.example.com  # exposed
VITE_APP_TITLE=My App        # exposed

Access in source:

console.log(import.meta.env.VITE_API_URL)
// → "https://api.example.com"

Warning

Never put secrets or credentials in VITE_* variables. They are embedded verbatim in your client bundle and visible to anyone inspecting your JavaScript.

Built-In Environment Variables

Variable Type Description
import.meta.env.MODE string 'development' or 'production' (or custom mode)
import.meta.env.BASE_URL string The base config value
import.meta.env.PROD boolean true in production
import.meta.env.DEV boolean true in development
import.meta.env.SSR boolean true in SSR mode

Custom Modes

Use --mode to load a custom .env file:

vite build --mode staging

This loads .env.staging and .env.staging.local.

JSON Imports

Import JSON files with named exports:

import pkg from './package.json'
console.log(pkg.version)

// Named imports (tree-shakeable)
import { version, name } from './package.json'
console.log(version)

When json.namedExports is true (default), individual fields can be imported and unused fields are tree-shaken.

When json.stringify is 'auto' (default), large JSON files are serialized as strings for better performance.

HTML and Multi-Page Apps

Vite uses index.html as the entry point. For multi-page apps, create multiple HTML files and configure build inputs:

project/
├── index.html
├── about.html
├── contact/
│   └── index.html
└── src/
    ├── main.js
    ├── about.js
    └── contact.js
import { defineConfig } from 'vite'
import path from 'node:path'

export default defineConfig({
  build: {
    rollupOptions: {
      input: {
        main: path.resolve(__dirname, 'index.html'),
        about: path.resolve(__dirname, 'about.html'),
        contact: path.resolve(__dirname, 'contact/index.html'),
      },
    },
  },
})

In each HTML file, reference its module with <script type="module">:

<!-- about.html -->
<!DOCTYPE html>
<html>
  <head><title>About</title></head>
  <body>
    <div id="app"></div>
    <script type="module" src="/src/about.js"></script>
  </body>
</html>

Server-Side Rendering (SSR)

Vite provides built-in SSR support. Create a server entry:

// src/entry-server.js
export function render(url) {
  // render app to HTML string
  return '<div id="app">...</div>'
}

Build the SSR bundle:

vite build --ssr src/entry-server.js

The SSR build outputs a CommonJS bundle suitable for server environments.

Module Runner

Vite v8 provides a Module Runner for SSR environments, allowing efficient server-side module execution with HMR support during development.

Source Maps

Enable source maps for production debugging:

vite build --sourcemap

Options:

export default defineConfig({
  build: {
    sourcemap: true,       // generate .map files
    // or: 'inline'        // inline source maps
    // or: 'hidden'        // .map files without sourceMappingURL
  },
})

Build Manifest

Generate a manifest file mapping original filenames to hashed output:

export default defineConfig({
  build: {
    manifest: true,
  },
})

Produces dist/.vite/manifest.json:

{
  "src/main.js": {
    "file": "assets/main-BfH3kQ2f.js",
    "src": "src/main.js",
    "isEntry": true,
    "css": ["assets/main-DiwrgTda.css"]
  }
}

This is essential for server-rendered apps that need to reference the correct hashed filenames.

Public Directory

Files in public/ are served at the root path and copied to dist/ unchanged:

public/
├── favicon.ico
├── robots.txt
└── images/
    └── og-image.png

Reference them with absolute paths (no import needed):

<link rel="icon" href="/favicon.ico" />
<img src="/images/og-image.png" />

Caution

Do not import files from public/ in JavaScript — they are not processed by Vite's module graph. Always use absolute URL paths like /filename instead.


6. Troubleshooting

Error: Cannot find module or Failed to resolve import

Cause: A dependency is missing or an import path is incorrect.

Solution:

  1. Run npm install to ensure all dependencies are installed
  2. Check for typos in the import path
  3. Verify resolve.alias mappings in vite.config.js
  4. Clear the dependency cache: vite --force

Prevention: Use TypeScript or IDE extensions that validate import paths at edit time.

Error: Pre-transform error / [plugin:vite:import-analysis] Failed to resolve import

Cause: Vite cannot resolve an imported module during dev server operation.

Solution:

  1. Check that the package is listed in dependencies or devDependencies
  2. For monorepo packages, ensure they are properly linked
  3. Try clearing pre-bundled dependencies:
rm -rf node_modules/.vite
npm run dev

Prevention: Keep package.json in sync with actual imports.

Error: Port already in use

Cause: Another process is using the default port (5173 for dev, 4173 for preview).

Solution:

vite --port 3000

Or use --strictPort to fail instead of auto-incrementing:

vite --strictPort --port 5173

Prevention: Configure a consistent port in vite.config.js.

HMR Not Working or Stops After Save

Cause: Several possibilities including circular dependencies, missing HMR boundary, or file system watcher issues.

Solution:

  1. Check the terminal for error messages
  2. For circular dependency issues (especially with TypeScript), refactor to break the cycle
  3. On WSL2 or networked drives, try polling mode by setting server.watch.usePolling: true
  4. Ensure your framework plugin is up to date

Prevention: Avoid circular imports. Keep framework plugins updated to match your Vite version.

Note

HMR may stop functioning after saving a TypeScript file that introduces a circular dependency. Vite detects these cycles but cannot always recover gracefully. Breaking the circular dependency resolves the issue.

CSS Ordering Differs Between Dev and Production

Cause: In development, CSS is loaded as individual modules via <style> tags. In production, CSS is extracted and combined, which can change cascade order.

Solution:

  1. Avoid relying on CSS import order for specificity conflicts
  2. Use more specific selectors or CSS Modules to isolate styles
  3. Explicitly control order with a single entry CSS file that @imports in the correct sequence

Prevention: Use CSS Modules, scoped styles, or a CSS-in-JS solution to avoid cascade ordering issues.

vite build Produces Empty or Incorrect Output

Cause: Entry point misconfiguration or emptyOutDir clearing a non-default output path.

Solution:

  1. Verify index.html exists at the project root (or configure build.rollupOptions.input)
  2. Check that build.outDir is correct
  3. Ensure <script type="module" src="..."> in your HTML points to an existing file

Dependency Pre-Bundling Fails or Loops

Cause: A dependency has unusual packaging, or Vite cannot detect all dependencies upfront.

Solution:

  1. Force re-bundling: vite --force
  2. Manually include or exclude packages:
export default defineConfig({
  optimizeDeps: {
    include: ['problematic-package'],
    exclude: ['package-with-its-own-esm'],
  },
})

Error: [vite] Internal server error: Missing "./package.json" export in "some-package"

Cause: The dependency's exports field in package.json does not include ./package.json.

Solution: Add the package to optimizeDeps.include to force pre-bundling, which resolves the export map.

Build Fails with Unexpected token in a Dependency

Cause: A dependency contains syntax that the current transpiler cannot handle (e.g., newer ECMAScript features in a CJS package).

Solution:

  1. Add the package to optimizeDeps.include
  2. If using oxc and it fails, try disabling it for the affected file:
export default defineConfig({
  optimizeDeps: {
    include: ['legacy-package'],
  },
})

Plugin Errors Are Hidden or Swallowed

Cause: Some plugin errors may not surface clearly in the console.

Solution:

  1. Run with debug logging: vite --debug
  2. Check if the plugin has a configResolved hook that might be silencing errors
  3. Try removing plugins one by one to isolate the issue
  4. Update the plugin to its latest version

Legacy Browser Support

For projects that must support older browsers (IE11, older Safari), use the official legacy plugin:

npm install -D @vitejs/plugin-legacy
import legacy from '@vitejs/plugin-legacy'

export default defineConfig({
  plugins: [
    legacy({
      targets: ['defaults', 'not IE 11'],
    }),
  ],
})

Warning

@vitejs/plugin-legacy v8.0.1 may encounter issues with esbuild 0.27.7 or later. Pin your esbuild version if you experience build failures with this plugin.


7. FAQ

CLI Reference

vite / vite dev / vite serve

Start the development server.

vite [options]
Flag Description
--host [host] Bind to specific host (0.0.0.0 for all interfaces)
--port <port> Set port number
--open [path] Open browser on start
--cors Enable CORS
--strictPort Fail if port is in use (instead of trying next)
--force Force dependency re-bundling
--config <file> Use specified config file
--base <path> Public base path
--logLevel <level> Log level: info, warn, error, silent
--clearScreen Allow/disallow clearing the terminal screen
--mode <mode> Set env mode (loads .env.<mode>)
--debug [feat] Show debug logs (optionally for specific features)
--filter <filter> Filter debug logs

vite build

Build for production.

vite build [options]
Flag Description
--outDir <dir> Output directory (default: dist)
--assetsInlineLimit <n> Asset inline threshold in bytes (default: 4096)
--ssr [entry] Build for SSR with given entry
--sourcemap Generate source maps
--minify [minifier] Enable/disable minification
--manifest [name] Generate build manifest
--ssrManifest [name] Generate SSR manifest
--emptyOutDir Clear output directory before build
--watch Rebuild on file changes
--target <target> Transpilation target
--config <file> Use specified config file
--base <path> Public base path
--logLevel <level> Log level
--clearScreen Allow/disallow clearing the terminal screen
--mode <mode> Set env mode

vite preview

Preview the production build locally.

vite preview [options]

Accepts --host, --port, --strictPort, --open, --config, --base, --logLevel, --clearScreen, and --mode.

vite optimize

Run dependency pre-bundling explicitly.

vite optimize [options]

Accepts --force, --config, and --logLevel.


Q: What is the difference between Vite and traditional bundlers like webpack?

Traditional bundlers process your entire application upfront before serving anything. Vite takes a different approach: it serves source files directly via native ES modules during development, only transforming individual files on demand. The result is near-instant dev server startup regardless of project size. For production, Vite uses Rolldown to produce optimized bundles.

Q: Does Vite support CommonJS dependencies?

Yes. Vite's dependency pre-bundling step automatically converts CommonJS and UMD packages to ESM. This happens transparently during dev server startup. In rare cases involving conditional CJS modules, you may need to add the dependency to optimizeDeps.include.

Q: Can I use Vite without a framework?

Absolutely. The vanilla and vanilla-ts templates create a plain JavaScript/TypeScript project with no framework. Vite works as a general-purpose dev server and bundler for any web project.

Q: How do I deploy a Vite app?

Run vite build to produce a dist/ directory with static files. Deploy these to any static hosting provider: Netlify, Vercel, Cloudflare Pages, GitHub Pages, AWS S3 + CloudFront, or any web server capable of serving static files.

Q: How do I add HTTPS to the dev server?

Configure the server.https option with your certificate and key files in vite.config.js. For local development, tools like mkcert can generate trusted local certificates.

Q: What is import.meta.glob and when should I use it?

import.meta.glob is Vite's way of importing multiple modules matching a file pattern. Common use cases: auto-registering Vue components, loading all route modules, collecting markdown files for a blog. It replaces webpack's require.context.

Q: What changed in Vite v8 compared to earlier versions?

Key changes in v8:

  • Rolldown replaces Rollup as the production bundler
  • oxc replaces esbuild as the default TypeScript/JSX transpiler
  • Node.js minimum version raised to ^20.19.0 or >=22.12.0
  • Module Preload Polyfill for legacy browsers
  • Forward Console feature to pipe browser logs to the terminal

Q: How do I handle environment-specific configuration?

Use the --mode flag to load different .env files. Create .env.development, .env.production, .env.staging, etc. Variables with the VITE_ prefix are exposed to client code; all others remain server-side only.

Q: My vendor bundle is too large. How do I split it?

Use build.rollupOptions.output.manualChunks to control code splitting:

export default defineConfig({
  build: {
    rollupOptions: {
      output: {
        manualChunks: {
          vendor: ['react', 'react-dom'],
          charts: ['d3', 'chart.js'],
        },
      },
    },
  },
})

Q: Can I use Vite for a library instead of an application?

Yes. Use build.lib to configure library mode. Vite will output ESM and UMD/CJS formats, externalize dependencies, and handle proper module exports. See the Library Mode section in this guide.

Q: How do I handle monorepo setups?

Vite works in monorepos. Use server.fs.allow to whitelist directories outside the project root. For linked packages, ensure they are properly listed in optimizeDeps.include if pre-bundling has trouble resolving them.

Q: Is there a way to analyze my bundle size?

Use rollup-plugin-visualizer (compatible with Vite via the plugin system):

npm install -D rollup-plugin-visualizer
import { visualizer } from 'rollup-plugin-visualizer'

export default defineConfig({
  plugins: [visualizer({ open: true })],
})

This generates an interactive treemap of your bundle after building.