GENERATION.md
# Generation Info - **Source:** `sources/vite` - **Git SHA:** `7139ef35cf6c9c0979385a467e8499fc19dfd099` - **Generated:** 2026-11-05
antfu/skills · GitHub
Vite 빌드 도구 구성, 플러그인 API, SSR 및 Vite 8 롤다운 마이그레이션. Vite 프로젝트, vite.config.ts, Vite 플러그인을 다루거나 Vite를 사용하여 라이브러리/SSR 앱을 빌드할 때 사용합니다.
프로젝트 폴더에서 아래 명령어를 실행하고, 설치할 에이전트를 선택하세요.
npx skills add antfu/skills --skill vite설치 명령을 직접 실행해야 적용됩니다. 지원 에이전트와 필요한 권한·라이선스는 제작자의 안내를 확인하세요.
GENERATION.md# Generation Info - **Source:** `sources/vite` - **Git SHA:** `7139ef35cf6c9c0979385a467e8499fc19dfd099` - **Generated:** 2026-11-05
references/build-and-ssr.md---
name: vite-build-ssr
description: Vite library mode, multi-page apps, JavaScript API, and SSR guidance
---
# Build and SSR
## Library Mode
Build a library for distribution:
```ts
// vite.config.ts
import { resolve } from 'node:path'
import { defineConfig } from 'vite'
export default defineConfig({
build: {
lib: {
entry: resolve(import.meta.dirname, 'lib/main.ts'),
name: 'MyLib',
fileName: 'my-lib',
},
rolldownOptions: {
external: ['vue', 'react'],
output: {
globals: {
vue: 'Vue',
react: 'React',
},
},
},
},
})
```
### Multiple Entries
```ts
build: {
lib: {
entry: {
'my-lib': resolve(import.meta.dirname, 'lib/main.ts'),
secondary: resolve(import.meta.dirname, 'lib/secondary.ts'),
},
name: 'MyLib',
},
}
```
### Output Formats
- Single entry: `es` and `umd`
- Multiple entries: `es` and `cjs`
### Package.json Setup
```json
{
"name": "my-lib",
"type": "module",
"files": ["dist"],
"main": "./dist/my-lib.umd.cjs",
"module": "./dist/my-lib.js",
"exports": {
".": {
"import": "./dist/my-lib.js",
"require": "./dist/my-lib.umd.cjs"
},
"./style.css": "./dist/my-lib.css"
}
}
```
## Multi-Page App
Prefer the top-level `input` option (Vite 8) — it applies in dev too:
```ts
export default defineConfig({
input: {
main: resolve(import.meta.dirname, 'index.html'),
nested: resolve(import.meta.dirname, 'nested/index.html'),
},
})
```
## SSR Development
**Note:** Vite's SSR support is **low-level** and designed mostly for meta-framework authors, not application developers. If you need SSR for your app, use a Vite-based meta-framework instead:
- **Nuxt** (Vue) - https://nuxt.com
- **SvelteKit** (Svelte) - https://svelte.dev/docs/kit
- **SolidStart** (Solid) - https://start.solidjs.com
- **TanStack Start** (React) - https://tanstack.com/start
These frameworks build on top of Vite's SSR primitives so you don't have to wire them up yourself.
**Need a server?** Consider [Nitro](https://nitro.build) -- think of it as "Vite for servers." Nitro provides a portable, framework-agnostic server layer with file-based API routing, auto-imports, and deployment presets for dozens of platforms (Node.js, Deno, Bun, Cloudflare Workers, Vercel, Netlify, etc.). It integrates naturally with Vite and is what powers Nuxt's server engine. See the [Nitro docs](https://nitro.build) for more details.
## JavaScript API
### createServer
```ts
import { createServer } from 'vite'
const server = await createServer({
configFile: false,
root: import.meta.dirname,
server: { port: 1337 },
})
await server.listen()
server.printUrls()
```
### build
Returns `RolldownOutput | RolldownOutput[] | RolldownWatcher` (was `RollupOutput` in Vite 7). On failure it throws a `BundleError` — inspect `e.errors`.
```ts
import { build } from 'vite'
await build({
root: './project',
build: { outDir: 'dist' },
})
```
### createBuilder (Vite 8, environment-aware)
Use `createBuilder` instead of `build` to build all configured environments (Environment API):
```ts
import { createBuilder } from 'vite'
const builder = await createBuilder()
await builder.buildApp()
```
### preview
```ts
import { preview } from 'vite'
const previewServer = await preview({
preview: { port: 8080, open: true },
})
previewServer.printUrls()
```
### resolveConfig
```ts
import { resolveConfig } from 'vite'
const config = await resolveConfig({}, 'build')
```
### loadEnv
```ts
import { loadEnv } from 'vite'
const env = loadEnv('development', process.cwd(), '')
// Loads all env vars (empty prefix = no filtering)
```
<!--
Source references:
- https://vite.dev/guide/build
- https://vite.dev/guide/api-javascript
- https://nitro.build
-->
references/core-config.md---
name: vite-config
description: Vite configuration patterns using vite.config.ts
---
# Vite Configuration
## Basic Setup
```ts
// vite.config.ts
import { defineConfig } from 'vite'
export default defineConfig({
// config options
})
```
Vite auto-resolves `vite.config.ts` from project root. Supports ES modules syntax regardless of `package.json` type.
## Conditional Config
Export a function to access command and mode:
```ts
export default defineConfig(({ command, mode, isSsrBuild, isPreview }) => {
if (command === 'serve') {
return { /* dev config */ }
} else {
return { /* build config */ }
}
})
```
- `command`: `'serve'` during dev, `'build'` for production
- `mode`: `'development'` or `'production'` (or custom via `--mode`)
## Async Config
```ts
export default defineConfig(async ({ command, mode }) => {
const data = await fetchSomething()
return { /* config */ }
})
```
## Using Environment Variables in Config
`.env` files are loaded **after** config resolution. Use `loadEnv` to access them in config:
```ts
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ mode }) => {
// Load env files from cwd, include all vars (empty prefix)
const env = loadEnv(mode, process.cwd(), '')
return {
define: {
__APP_ENV__: JSON.stringify(env.APP_ENV),
},
server: {
port: env.APP_PORT ? Number(env.APP_PORT) : 5173,
},
}
})
```
## Key Config Options
### input (top-level, Vite 8)
Declare entry points once at the top level. Acts as the default for `build.rolldownOptions.input`, `build.lib.entry`, `build.ssr` (when `true`), and `optimizeDeps.entries`. Useful when the app has no `index.html` entry.
```ts
export default defineConfig({
input: 'src/main.ts',
// or multi-page: { main: 'index.html', nested: 'nested/index.html' }
})
```
### resolve.alias
```ts
export default defineConfig({
resolve: {
alias: {
'@': '/src',
'~': '/src',
},
},
})
```
The array form's `customResolver` was removed in Vite 8 — use a custom plugin with a `resolveId` hook and `enforce: 'pre'` instead.
### define (Global Constants)
```ts
export default defineConfig({
define: {
__APP_VERSION__: JSON.stringify('1.0.0'),
__API_URL__: 'window.__backend_api_url',
},
})
```
Values must be JSON-serializable or single identifiers. Non-strings auto-wrapped with `JSON.stringify`.
### plugins
```ts
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
})
```
Plugins array is flattened; falsy values ignored.
### server.proxy
```ts
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
})
```
### build.target
Default `'baseline-widely-available'` → `['chrome111', 'edge111', 'firefox114', 'safari16.4', 'ios16.4']` (Vite 8, Baseline Widely Available as of 2026-01-01). Customize:
```ts
export default defineConfig({
build: {
target: 'esnext', // or 'es2020', ['chrome90', 'firefox88']
},
})
```
To set the dev/transform target, use `oxc.target` (replaces `esbuild.target`); `build.target` takes precedence for builds.
### tsconfig (Vite 8)
Force a specific tsconfig instead of Vite's per-file discovery. Discouraged — prefer placing `tsconfig.json` near the files it configures and using TS `references`.
```ts
export default defineConfig({ tsconfig: './tsconfig.app.json' })
```
### devtools (Vite 8, experimental)
Enable [Vite DevTools](https://github.com/vitejs/devtools) integration (requires `@vitejs/devtools*` packages). Cannot be set from a plugin `config` hook.
```ts
export default defineConfig({ devtools: { apply: 'serve' } })
```
## TypeScript Intellisense
For plain JS config files:
```js
/** @type {import('vite').UserConfig} */
export default {
// ...
}
```
Or use `satisfies`:
```ts
import type { UserConfig } from 'vite'
export default {
// ...
} satisfies UserConfig
```
<!--
Source references:
- https://vite.dev/config/
- https://vite.dev/guide/
-->
references/core-features.md---
name: vite-features
description: Vite-specific import patterns and runtime features
---
# Vite Features
> Vite 8: TS/JSX transforms run on **Oxc** (`oxc` option, not `esbuild`), dep pre-bundling on **Rolldown**, and CSS minification on **Lightning CSS** by default. `esbuild`/`rollupOptions` still work as deprecated aliases.
## Glob Import
Import multiple modules matching a pattern:
```ts
const modules = import.meta.glob('./dir/*.ts')
// { './dir/foo.ts': () => import('./dir/foo.ts'), ... }
for (const path in modules) {
modules[path]().then((mod) => {
console.log(path, mod)
})
}
```
### Eager Loading
```ts
const modules = import.meta.glob('./dir/*.ts', { eager: true })
// Modules loaded immediately, no dynamic import
```
### Named Imports
```ts
const modules = import.meta.glob('./dir/*.ts', { import: 'setup' })
// Only imports the 'setup' export from each module
const defaults = import.meta.glob('./dir/*.ts', { import: 'default', eager: true })
```
### Multiple Patterns
```ts
const modules = import.meta.glob(['./dir/*.ts', './another/*.ts'])
```
### Negative Patterns
```ts
const modules = import.meta.glob(['./dir/*.ts', '!**/ignored.ts'])
```
### Custom Queries
```ts
const svgRaw = import.meta.glob('./icons/*.svg', { query: '?raw', import: 'default' })
const svgUrls = import.meta.glob('./icons/*.svg', { query: '?url', import: 'default' })
```
### Case-Insensitive Matching (Vite 8)
```ts
// matches Module.js, module.js, MODULE.js
const modules = import.meta.glob('./dir/module*.js', { caseSensitive: false })
```
## Asset Import Queries
### URL Import
```ts
import imgUrl from './img.png'
// Returns resolved URL: '/src/img.png' (dev) or '/assets/img.2d8efhg.png' (build)
```
### Explicit URL
```ts
import workletUrl from './worklet.js?url'
```
### Raw String
```ts
import shaderCode from './shader.glsl?raw'
```
### Inline/No-Inline
```ts
import inlined from './small.png?inline' // Force base64 inline
import notInlined from './large.png?no-inline' // Force separate file
```
### Web Workers
```ts
import Worker from './worker.ts?worker'
const worker = new Worker()
// Or inline:
import InlineWorker from './worker.ts?worker&inline'
```
Preferred pattern using constructor:
```ts
const worker = new Worker(new URL('./worker.ts', import.meta.url), {
type: 'module',
})
```
## Environment Variables
### Built-in Constants
```ts
import.meta.env.MODE // 'development' | 'production' | custom
import.meta.env.BASE_URL // Base URL from config
import.meta.env.PROD // true in production
import.meta.env.DEV // true in development
import.meta.env.SSR // true when running in server
```
### Custom Variables
Only `VITE_` prefixed vars exposed to client:
```
# .env
VITE_API_URL=https://api.example.com
DB_PASSWORD=secret # NOT exposed to client
```
```ts
console.log(import.meta.env.VITE_API_URL) // works
console.log(import.meta.env.DB_PASSWORD) // undefined
```
### Mode-specific Files
```
.env # always loaded
.env.local # always loaded, gitignored
.env.[mode] # only in specified mode
.env.[mode].local # only in specified mode, gitignored
```
### TypeScript Support
```ts
// vite-env.d.ts
interface ImportMetaEnv {
readonly VITE_API_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
```
### HTML Replacement
```html
<p>Running in %MODE%</p>
<script>window.API = "%VITE_API_URL%"</script>
```
## CSS Modules
Any `.module.css` file treated as CSS module:
```ts
import styles from './component.module.css'
element.className = styles.button
```
With camelCase conversion:
```ts
// .my-class -> myClass (if css.modules.localsConvention configured)
import { myClass } from './component.module.css'
```
## JSON Import
```ts
import pkg from './package.json'
import { version } from './package.json' // Named import with tree-shaking
```
## HMR API
```ts
if (import.meta.hot) {
import.meta.hot.accept((newModule) => {
// Handle update
})
import.meta.hot.dispose((data) => {
// Cleanup before module is replaced
})
import.meta.hot.invalidate() // Force full reload
}
```
## WebAssembly
### ESM Integration (Vite 8)
Import a `.wasm` file directly — Vite instantiates it and re-exposes its exports as named ESM exports. Behaves as an async module (needs top-level `await`):
```js
import { add } from './add.wasm'
console.log(add(1, 2)) // 3
```
For TS support, enable `allowArbitraryExtensions` and add `add.d.wasm.ts`. SSR builds rely on `node:fs`, so this only works in Node-compatible runtimes.
### Manual Initialization
```js
import init from './example.wasm?init'
const instance = await init({ imports: { /* ... */ } })
instance.exports.test()
```
## Chunk Import Map Optimization (Vite 8, experimental)
Set `build.chunkImportMap: true` to reference chunks by stable ID via an import map instead of hashed URLs. Prevents cascading cache invalidation (updating a leaf chunk no longer invalidates its importers). Requires `import.meta.resolve` support (use `@vitejs/plugin-legacy` for older browsers).
<!--
Source references:
- https://vite.dev/guide/features
- https://vite.dev/guide/env-and-mode
- https://vite.dev/guide/assets
- https://vite.dev/guide/api-hmr
-->
references/core-plugin-api.md---
name: vite-plugin-api
description: Vite plugin authoring with Vite-specific hooks
---
# Vite Plugin API
Vite plugins extend Rolldown's plugin interface with Vite-specific hooks.
## Basic Structure
```ts
function myPlugin(): Plugin {
return {
name: 'my-plugin',
// hooks...
}
}
```
## Vite-Specific Hooks
### config
Modify config before resolution:
```ts
const plugin = () => ({
name: 'add-alias',
config: () => ({
resolve: {
alias: { foo: 'bar' },
},
}),
})
```
### configResolved
Access final resolved config:
```ts
const plugin = () => {
let config: ResolvedConfig
return {
name: 'read-config',
configResolved(resolvedConfig) {
config = resolvedConfig
},
transform(code, id) {
if (config.command === 'serve') { /* dev */ }
},
}
}
```
### configureServer
Add custom middleware to dev server:
```ts
const plugin = () => ({
name: 'custom-middleware',
configureServer(server) {
server.middlewares.use((req, res, next) => {
// handle request
next()
})
},
})
```
Return function to run **after** internal middlewares:
```ts
configureServer(server) {
return () => {
server.middlewares.use((req, res, next) => {
// runs after Vite's middlewares
})
}
}
```
### transformIndexHtml
Transform HTML entry files:
```ts
const plugin = () => ({
name: 'html-transform',
transformIndexHtml(html) {
return html.replace(/<title>(.*?)<\/title>/, '<title>New Title</title>')
},
})
```
Inject tags:
```ts
transformIndexHtml() {
return [
{ tag: 'script', attrs: { src: '/inject.js' }, injectTo: 'body' },
]
}
```
### handleHotUpdate
Custom HMR handling:
```ts
handleHotUpdate({ server, modules, timestamp }) {
server.ws.send({ type: 'custom', event: 'special-update', data: {} })
return [] // empty = skip default HMR
}
```
## Virtual Modules
Serve virtual content without files on disk:
```ts
const plugin = () => {
const virtualModuleId = 'virtual:my-module'
const resolvedId = '\0' + virtualModuleId
return {
name: 'virtual-module',
resolveId(id) {
if (id === virtualModuleId) return resolvedId
},
load(id) {
if (id === resolvedId) {
return `export const msg = "from virtual module"`
}
},
}
}
```
Usage:
```ts
import { msg } from 'virtual:my-module'
```
Convention: prefix user-facing path with `virtual:`, prefix resolved id with `\0`.
## Plugin Ordering
Use `enforce` to control execution order:
```ts
{
name: 'pre-plugin',
enforce: 'pre', // runs before core plugins
}
{
name: 'post-plugin',
enforce: 'post', // runs after build plugins
}
```
Order: Alias → `enforce: 'pre'` → Core → User (no enforce) → Build → `enforce: 'post'` → Post-build
## Conditional Application
```ts
{
name: 'build-only',
apply: 'build', // or 'serve'
}
// Function form:
{
apply(config, { command }) {
return command === 'build' && !config.build.ssr
}
}
```
## Rolldown Hooks (dev + build)
These come from Rolldown and are **per-environment** (`this.environment` available):
- `resolveId(id, importer)` - Resolve import paths
- `load(id)` - Load module content
- `transform(code, id)` - Transform module code
```ts
transform(code, id) {
if (id.endsWith('.custom')) {
return { code: compile(code), map: null }
}
}
```
### Hook Filters (Vite 8)
Prefer the object form with `filter` + `handler` for `transform`/`resolveId`/`load` — filtering runs in Rust, avoiding a JS call per module:
```ts
import { exactRegex } from '@rolldown/pluginutils' // also from 'rolldown/filter'
const plugin = () => ({
name: 'transform-file',
transform: {
filter: { id: /\.custom$/ },
handler(code, id) {
return { code: compile(code), map: null }
},
},
})
```
`@rolldown/pluginutils` exports helpers like `exactRegex` and `prefixRegex`.
## Per-Environment vs Global Hooks (Vite 8)
- **Global** (called once, no `this.environment`): `config`, `configResolved`, `configureServer`, `configurePreviewServer`, `closeServer`, `closePreviewServer`, `buildApp`.
- **Per-environment** (called per environment, expose `this.environment`): all Rolldown hooks, `transformIndexHtml`, `handleHotUpdate`/`hotUpdate`, `configEnvironment`, `applyToEnvironment`.
## Cleanup Hooks (Vite 8)
`closeServer({ reason })` runs after the dev server is torn down (`reason` is `'restart'` or `'close'`); `closePreviewServer()` is the preview equivalent. Use to dispose resources created in `configureServer`.
```ts
{
name: 'close-server',
configureServer(server) { this.resource = createResource() },
async closeServer({ reason }) {
if (reason === 'close') await this.resource.dispose()
},
}
```
## Plugin Context Meta (Vite 8)
- `this.meta.viteVersion` — current Vite version string.
- `this.meta.rolldownVersion` — only defined on Rolldown-powered Vite (8+); use it to branch behavior.
## Output Bundle Metadata
During build, Vite augments Rolldown output objects with `viteMetadata` — inspect emitted CSS/assets without `build.manifest`:
```ts
{
name: 'output-metadata',
enforce: 'post',
generateBundle(_, bundle) {
for (const output of Object.values(bundle)) {
const css = output.viteMetadata?.importedCss // Set<string>
const assets = output.viteMetadata?.importedAssets // Set<string>
}
},
}
```
## Referencing Emitted Assets
`this.emitFile({ type: 'asset', ... })` returns a `referenceId`; resolve its final URL later:
- In JS: `import.meta.ROLLDOWN_FILE_URL_<referenceId>`
- In CSS/HTML: `__VITE_ASSET__<referenceId>__`
## Client-Server Communication
Server to client:
```ts
configureServer(server) {
server.ws.send('my:event', { msg: 'hello' })
}
```
Client side:
```ts
if (import.meta.hot) {
import.meta.hot.on('my:event', (data) => {
console.log(data.msg)
})
}
```
Client to server:
```ts
// Client
import.meta.hot.send('my:from-client', { msg: 'Hey!' })
// Server
server.ws.on('my:from-client', (data, client) => {
client.send('my:ack', { msg: 'Got it!' })
})
```
<!--
Source references:
- https://vite.dev/guide/api-plugin
-->
references/environment-api.md---
name: vite-environment-api
description: Vite 6+ Environment API for multiple runtime environments
---
# Environment API (Vite 6+)
The Environment API formalizes multiple runtime environments beyond the traditional client/SSR split.
## Concept
Before Vite 6: Two implicit environments (`client` and `ssr`).
Vite 6+: Configure as many environments as needed (browser, node server, edge server, etc.).
## Basic Configuration
For SPA/MPA, nothing changes—options apply to the implicit `client` environment:
```ts
export default defineConfig({
build: { sourcemap: false },
optimizeDeps: { include: ['lib'] },
})
```
## Multiple Environments
```ts
export default defineConfig({
build: { sourcemap: false }, // Inherited by all environments
optimizeDeps: { include: ['lib'] }, // Client only
environments: {
// SSR environment
server: {},
// Edge runtime environment
edge: {
resolve: { noExternal: true },
},
},
})
```
Environments inherit top-level config. Some options (like `optimizeDeps`) only apply to `client` by default.
## Environment Options
```ts
interface EnvironmentOptions {
define?: Record<string, any>
resolve?: EnvironmentResolveOptions
optimizeDeps: DepOptimizationOptions
consumer?: 'client' | 'server'
dev: DevOptions
build: BuildOptions
}
```
## Custom Environment Instances
Runtime providers can define custom environments:
```ts
import { customEnvironment } from 'vite-environment-provider'
export default defineConfig({
environments: {
ssr: customEnvironment({
build: { outDir: '/dist/ssr' },
}),
},
})
```
Example: Cloudflare's Vite plugin runs code in `workerd` runtime during development.
## Dev Environment Types
Environments run modules through a runner. Two dev environment shapes exist:
- **`RunnableDevEnvironment`** — evaluates modules in the **same runtime** as the Vite server, so it exchanges arbitrary JS values in-process. Its `runner.import(url)` is the modern replacement for `server.ssrLoadModule` (full HMR). Default for `ssr` and other non-client envs. Guard with `isRunnableDevEnvironment(env)`.
```ts
if (isRunnableDevEnvironment(server.environments.ssr)) {
const mod = await server.environments.ssr.runner.import('/src/entry-server.ts')
}
```
- **`FetchableDevEnvironment`** — communicates only via the **Fetch API** (`Request`/`Response`), so it works for runtimes that can't run Vite directly (e.g. Cloudflare Workers). Preferred over `RunnableDevEnvironment` for such runtimes. Handle requests with `handleRequest`; dispatch via `env.dispatchFetch(new Request(url))`.
## Building Environments
`vite build` / `vite build --ssr` still build only client/ssr for back-compat. Setting the `builder` option (even `{}`, which is what `vite build --app` does) opts into building **all** environments.
```js [build.js]
import { createBuilder } from 'vite'
const builder = await createBuilder() // build-time equivalent of createServer
await builder.buildApp() // build every configured environment
// await builder.build(builder.environments.ssr) // or one at a time
```
`createBuilder` supersedes the standalone `build()` for environment-aware builds. Customize with the `builder.buildApp` config option or the global `buildApp` plugin hook (use `environment.isBuilt` to skip already-built envs).
## Per-Environment Plugins
Restrict a plugin to specific environments with `applyToEnvironment`, or configure each env with the `configEnvironment` hook (set defaults in `config`, since the full env list isn't known there):
```ts
{
name: 'ssr-only',
applyToEnvironment(env) { return env.name === 'ssr' },
}
```
## Backward Compatibility
- `server.moduleGraph` returns mixed client/SSR view
- `ssrLoadModule` still works
- Existing SSR apps work unchanged
## When to Use
- **End users**: Usually don't need to configure—frameworks handle it
- **Plugin authors**: Use for environment-aware transformations
- **Framework authors**: Create custom environments for their runtime needs
## Plugin Environment Access
Plugins can access environment in hooks:
```ts
{
name: 'env-aware',
transform(code, id, options) {
if (options?.ssr) {
// SSR-specific transform
}
},
}
```
<!--
Source references:
- https://vite.dev/guide/api-environment
- https://vite.dev/guide/api-environment-frameworks
- https://vite.dev/guide/api-environment-plugins
- https://vite.dev/guide/api-environment-runtimes
- https://vite.dev/blog/announcing-vite6
-->
references/rolldown-migration.md---
name: vite-rolldown
description: Vite 8 Rolldown bundler and Oxc transformer migration
---
# Rolldown Migration (Vite 8)
Vite 8 replaces esbuild + Rollup with [Rolldown](https://rolldown.rs/) (Rust bundler) and [Oxc](https://oxc.rs/) (Rust transformer/minifier).
## What Changed
| Before (Vite 7) | After (Vite 8) |
|-----------------|----------------|
| esbuild (dev/JS transform) | Oxc Transformer |
| esbuild (dep pre-bundling) | Rolldown |
| esbuild (CSS minify) | Lightning CSS (default) |
| Rollup (production build) | Rolldown |
| `build.rollupOptions` | `build.rolldownOptions` |
| `worker.rollupOptions` | `worker.rolldownOptions` |
| `optimizeDeps.esbuildOptions` | `optimizeDeps.rolldownOptions` |
| `esbuild` option | `oxc` option |
## Backward Compatibility (Auto-Conversion)
Old options are **deprecated but still work** — Vite converts them internally:
- `esbuild` → `oxc`
- `build.rollupOptions` → `build.rolldownOptions` (alias)
- `optimizeDeps.esbuildOptions` → `optimizeDeps.rolldownOptions`
Migrate to the new names; the compat shims will be removed in a future major. Read the converted values from the `configResolved` hook (`config.oxc`, `config.optimizeDeps.rolldownOptions`).
## Default Browser Target Change
`build.target` / `'baseline-widely-available'` moved to newer versions (Baseline Widely Available as of 2026-01-01):
- Chrome 107 → 111, Edge 107 → 111, Firefox 104 → 114, Safari 16.0 → 16.4
- Resolves to `['chrome111', 'edge111', 'firefox114', 'safari16.4', 'ios16.4']`
- Default `--minify` is now `oxc` (was `esbuild`)
## Config Migration
### rollupOptions → rolldownOptions
```ts
export default defineConfig({
build: {
rolldownOptions: { // was rollupOptions
external: ['vue'],
output: { globals: { vue: 'Vue' } },
},
},
})
```
Prefer the top-level [`input`](../references/core-config.md) option over `build.rolldownOptions.input` — it applies in dev too.
### esbuild → oxc
```ts
export default defineConfig({
oxc: { // was esbuild
jsx: {
runtime: 'classic',
pragma: 'h',
pragmaFrag: 'Fragment',
},
},
})
```
`esbuild.jsxFactory`→`oxc.jsx.pragma`, `jsxFragment`→`oxc.jsx.pragmaFrag`, `jsx: 'transform'`→`oxc.jsx: { runtime: 'classic' }`, `jsx: 'automatic'`→`{ runtime: 'automatic' }`. `esbuild.banner`/`footer` have no equivalent — use a custom plugin `transform` hook.
### JSX Configuration
```ts
export default defineConfig({
oxc: {
jsx: {
runtime: 'automatic', // or 'classic'
importSource: 'react', // for automatic runtime
},
jsxInject: `import React from 'react'`, // auto-inject
},
})
```
### Custom Transform Targets
```ts
export default defineConfig({
oxc: {
include: ['**/*.ts', '**/*.tsx'],
exclude: ['node_modules/**'],
},
})
```
## Notable Breaking Changes
- **`build()` throws `BundleError`** (JS API): typed `Error & { errors?: RolldownError[] }`. Access individual errors via `e.errors`.
- **`build.rollupOptions.output.manualChunks`**: object form removed, function form deprecated. Use Rolldown's [`codeSplitting`](https://rolldown.rs/reference/OutputOptions.codeSplitting).
- **`build.rollupOptions.watch.chokidar`** removed → `build.rolldownOptions.watch.watcher`.
- **`resolve.alias[].customResolver`** deprecated → use a custom plugin with a `resolveId` hook and `enforce: 'pre'`.
- **`build.commonjsOptions`** is now a no-op.
- **`require` calls for externalized modules** are preserved as `require`. To convert to `import`, use the re-exported `esmExternalRequirePlugin`.
- **`import.meta.hot.accept(url)`** no longer accepts a URL — pass an id.
- Unsupported by Rolldown: output format `'system'`/`'amd'`, `shouldTransformCachedModule`, `resolveImportMeta`, `renderDynamicImport`, `resolveFileUrl` hooks.
- Native decorator lowering is not yet supported by Oxc — use `@rolldown/plugin-babel` or `@rollup/plugin-swc` as a workaround.
## Detecting Rolldown-powered Vite
```ts
buildStart() {
// only defined on Vite 8+ (Rolldown)
if (this.meta.rolldownVersion) { /* ... */ }
}
```
Also exported from `vite`: `version`, `rolldownVersion` (`esbuildVersion`/`rollupVersion` kept only for back-compat).
## Plugin Compatibility
Most Vite plugins work unchanged — Rolldown supports Rollup's plugin API. Rolldown runs all parallel Rollup hooks sequentially. If a plugin only works during build:
```ts
{
...rollupPlugin(),
enforce: 'post',
apply: 'build',
}
```
## New Capabilities
- Full bundle mode (experimental, `vite --experimentalBundle`)
- Chunk import map optimization (`build.chunkImportMap`)
- More flexible chunk splitting / Module Federation support
## Gradual Migration
`rolldown-vite` is Vite 7 running on Rolldown *without* other Vite 8 changes — an intermediate step:
```bash
# Step 1: swap vite for rolldown-vite on Vite 7 (npm alias in package.json)
# "vite": "npm:rolldown-vite@^7"
# Step 2: undo the alias and upgrade to Vite 8
pnpm add -D vite@8
```
If migrating from `rolldown-vite`, only the "NRV"-badged sections of the official migration guide apply.
## Overriding Vite in Frameworks
When a framework depends on older Vite:
```json
{
"pnpm": {
"overrides": {
"vite": "8.0.0"
}
}
}
```
<!--
Source references:
- https://vite.dev/guide/migration
- https://vite.dev/blog/announcing-vite8
- https://vite.dev/config/shared-options#oxc
-->
SKILL.md---
name: vite
description: Vite build tool configuration, plugin API, SSR, and Vite 8 Rolldown migration. Use when working with Vite projects, vite.config.ts, Vite plugins, or building libraries/SSR apps with Vite.
metadata:
author: Anthony Fu
version: "2026.11.5"
source: Generated from https://github.com/vitejs/vite, scripts at https://github.com/antfu/skills
---
# Vite
> Based on Vite v8.3.5 (Rolldown-powered), generated at 2026-11-05. Vite 8 uses the Rolldown bundler and the Oxc transformer instead of esbuild + Rollup.
Vite is a next-generation frontend build tool with fast dev server (native ESM + HMR) and optimized production builds.
## Preferences
- Use TypeScript: prefer `vite.config.ts`
- Always use ESM, avoid CommonJS
## Core
| Topic | Description | Reference |
|-------|-------------|-----------|
| Configuration | `vite.config.ts`, `defineConfig`, conditional configs, `loadEnv` | [core-config](references/core-config.md) |
| Features | `import.meta.glob`, asset queries (`?raw`, `?url`), `import.meta.env`, HMR API | [core-features](references/core-features.md) |
| Plugin API | Vite-specific hooks, virtual modules, plugin ordering | [core-plugin-api](references/core-plugin-api.md) |
## Build & SSR
| Topic | Description | Reference |
|-------|-------------|-----------|
| Build & SSR | Library mode, SSR middleware mode, `ssrLoadModule`, JavaScript API | [build-and-ssr](references/build-and-ssr.md) |
## Advanced
| Topic | Description | Reference |
|-------|-------------|-----------|
| Environment API | Vite 6+ multi-environment support, custom runtimes | [environment-api](references/environment-api.md) |
| Rolldown Migration | Vite 8 changes: Rolldown bundler, Oxc transformer, config migration | [rolldown-migration](references/rolldown-migration.md) |
## Quick Reference
### CLI Commands
```bash
vite # Start dev server
vite build # Production build
vite preview # Preview production build
vite build --ssr # SSR build
```
### Common Config
```ts
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [],
resolve: { alias: { '@': '/src' } },
server: { port: 3000, proxy: { '/api': 'http://localhost:8080' } },
build: { target: 'esnext', outDir: 'dist' },
})
```
### Official Plugins
- `@vitejs/plugin-vue` - Vue 3 SFC support
- `@vitejs/plugin-vue-jsx` - Vue 3 JSX
- `@vitejs/plugin-react` - React with Oxc/Babel
- `@vitejs/plugin-react-swc` - React with SWC
- `@vitejs/plugin-legacy` - Legacy browser support