
Vite
- 31.9k installs
- 5.7k repo stars
- Updated June 23, 2026
- antfu/skills
vite is a skill for configuring Vite build tool for libraries, multi-page apps, and SSR.
About
Vite configuration patterns for library mode, multi-page applications, server-side rendering, and JavaScript API usage. Covers vite.config.ts setup, output format selection, and guidance on using meta-frameworks for SSR.
- Library mode and multi-page app configuration patterns
- SSR setup guidance (low-level primitives and meta-framework references)
- JavaScript API for programmatic Vite access (createServer, build, preview)
Vite by the numbers
- 31,908 all-time installs (skills.sh)
- +700 installs in the week ending Aug 4, 2026 (Skillselion tracking)
- Ranked #23 of 2,245 Frontend Development skills by installs in the Skillselion catalog
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/antfu/skills --skill viteAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 31.9k |
|---|---|
| repo stars | ★ 5.7k |
| Security audit | 3 / 3 scanners passed |
| Last updated | June 23, 2026 |
| Repository | antfu/skills ↗ |
How do you configure Vite library mode and SSR?
Vite configuration patterns for library mode, multi-page applications, server-side rendering, and JavaScript API usage. Covers vite.config.ts setup, output format selection, and guidance on using met
Who is it for?
Frontend developers configuring Vite 8 projects, library packages, SSR apps, or custom Vite plugins in TypeScript.
Skip if: Teams on webpack or Parcel-only stacks without Vite in the build pipeline.
When should I use this skill?
User edits vite.config.ts, asks about Vite SSR, library mode, Rolldown migration, or Vite plugin API hooks.
What you get
Working vite.config.ts patterns for library builds, SSR, plugins, and Vite 8 Rolldown migration.
- vite.config.ts patterns
- SSR build setup
- Plugin API examples
By the numbers
- Skill version 2026.1.31 generated 2026-01-31 from vitejs/vite
- Covers six reference topic files including Rolldown migration
- Documents four primary Vite CLI commands including vite build --ssr
Files
Vite
Based on Vite 8 beta (Rolldown-powered). Vite 8 uses Rolldown bundler and Oxc transformer.
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 |
| Features | import.meta.glob, asset queries (?raw, ?url), import.meta.env, HMR API | core-features |
| Plugin API | Vite-specific hooks, virtual modules, plugin ordering | core-plugin-api |
Build & SSR
| Topic | Description | Reference |
|---|---|---|
| Build & SSR | Library mode, SSR middleware mode, ssrLoadModule, JavaScript API | build-and-ssr |
Advanced
| Topic | Description | Reference |
|---|---|---|
| Environment API | Vite 6+ multi-environment support, custom runtimes | environment-api |
| Rolldown Migration | Vite 8 changes: Rolldown bundler, Oxc transformer, config migration | rolldown-migration |
Quick Reference
CLI Commands
vite # Start dev server
vite build # Production build
vite preview # Preview production build
vite build --ssr # SSR buildCommon Config
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
Generation Info
- Source:
sources/vite - Git SHA:
c47015eba4f0de255218c35769628d87152216ca - Generated: 2026-01-31
Build and SSR
Library Mode
Build a library for distribution:
// 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
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:
esandumd - Multiple entries:
esandcjs
Package.json Setup
{
"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
export default defineConfig({
build: {
rolldownOptions: {
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 -- 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 for more details.
JavaScript API
createServer
import { createServer } from 'vite'
const server = await createServer({
configFile: false,
root: import.meta.dirname,
server: { port: 1337 },
})
await server.listen()
server.printUrls()build
import { build } from 'vite'
await build({
root: './project',
build: { outDir: 'dist' },
})preview
import { preview } from 'vite'
const previewServer = await preview({
preview: { port: 8080, open: true },
})
previewServer.printUrls()resolveConfig
import { resolveConfig } from 'vite'
const config = await resolveConfig({}, 'build')loadEnv
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
-->
Vite Configuration
Basic Setup
// 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:
export default defineConfig(({ command, mode, isSsrBuild, isPreview }) => {
if (command === 'serve') {
return { /* dev config */ }
} else {
return { /* build config */ }
}
})command:'serve'during dev,'build'for productionmode:'development'or'production'(or custom via--mode)
Async Config
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:
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
resolve.alias
export default defineConfig({
resolve: {
alias: {
'@': '/src',
'~': '/src',
},
},
})define (Global Constants)
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
import vue from '@vitejs/plugin-vue'
export default defineConfig({
plugins: [vue()],
})Plugins array is flattened; falsy values ignored.
server.proxy
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:3000',
changeOrigin: true,
rewrite: (path) => path.replace(/^\/api/, ''),
},
},
},
})build.target
Default: Baseline Widely Available browsers. Customize:
export default defineConfig({
build: {
target: 'esnext', // or 'es2020', ['chrome90', 'firefox88']
},
})TypeScript Intellisense
For plain JS config files:
/** @type {import('vite').UserConfig} */
export default {
// ...
}Or use satisfies:
import type { UserConfig } from 'vite'
export default {
// ...
} satisfies UserConfig<!-- Source references:
- https://vite.dev/config/
- https://vite.dev/guide/
-->
Vite Features
Glob Import
Import multiple modules matching a pattern:
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
const modules = import.meta.glob('./dir/*.ts', { eager: true })
// Modules loaded immediately, no dynamic importNamed Imports
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
const modules = import.meta.glob(['./dir/*.ts', './another/*.ts'])Negative Patterns
const modules = import.meta.glob(['./dir/*.ts', '!**/ignored.ts'])Custom Queries
const svgRaw = import.meta.glob('./icons/*.svg', { query: '?raw', import: 'default' })
const svgUrls = import.meta.glob('./icons/*.svg', { query: '?url', import: 'default' })Asset Import Queries
URL Import
import imgUrl from './img.png'
// Returns resolved URL: '/src/img.png' (dev) or '/assets/img.2d8efhg.png' (build)Explicit URL
import workletUrl from './worklet.js?url'Raw String
import shaderCode from './shader.glsl?raw'Inline/No-Inline
import inlined from './small.png?inline' // Force base64 inline
import notInlined from './large.png?no-inline' // Force separate fileWeb Workers
import Worker from './worker.ts?worker'
const worker = new Worker()
// Or inline:
import InlineWorker from './worker.ts?worker&inline'Preferred pattern using constructor:
const worker = new Worker(new URL('./worker.ts', import.meta.url), {
type: 'module',
})Environment Variables
Built-in Constants
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 serverCustom Variables
Only VITE_ prefixed vars exposed to client:
# .env
VITE_API_URL=https://api.example.com
DB_PASSWORD=secret # NOT exposed to clientconsole.log(import.meta.env.VITE_API_URL) // works
console.log(import.meta.env.DB_PASSWORD) // undefinedMode-specific Files
.env # always loaded
.env.local # always loaded, gitignored
.env.[mode] # only in specified mode
.env.[mode].local # only in specified mode, gitignoredTypeScript Support
// vite-env.d.ts
interface ImportMetaEnv {
readonly VITE_API_URL: string
}
interface ImportMeta {
readonly env: ImportMetaEnv
}HTML Replacement
<p>Running in %MODE%</p>
<script>window.API = "%VITE_API_URL%"</script>CSS Modules
Any .module.css file treated as CSS module:
import styles from './component.module.css'
element.className = styles.buttonWith camelCase conversion:
// .my-class -> myClass (if css.modules.localsConvention configured)
import { myClass } from './component.module.css'JSON Import
import pkg from './package.json'
import { version } from './package.json' // Named import with tree-shakingHMR API
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
}<!-- 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
-->
Vite Plugin API
Vite plugins extend Rolldown's plugin interface with Vite-specific hooks.
Basic Structure
function myPlugin(): Plugin {
return {
name: 'my-plugin',
// hooks...
}
}Vite-Specific Hooks
config
Modify config before resolution:
const plugin = () => ({
name: 'add-alias',
config: () => ({
resolve: {
alias: { foo: 'bar' },
},
}),
})configResolved
Access final resolved config:
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:
const plugin = () => ({
name: 'custom-middleware',
configureServer(server) {
server.middlewares.use((req, res, next) => {
// handle request
next()
})
},
})Return function to run after internal middlewares:
configureServer(server) {
return () => {
server.middlewares.use((req, res, next) => {
// runs after Vite's middlewares
})
}
}transformIndexHtml
Transform HTML entry files:
const plugin = () => ({
name: 'html-transform',
transformIndexHtml(html) {
return html.replace(/<title>(.*?)<\/title>/, '<title>New Title</title>')
},
})Inject tags:
transformIndexHtml() {
return [
{ tag: 'script', attrs: { src: '/inject.js' }, injectTo: 'body' },
]
}handleHotUpdate
Custom HMR handling:
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:
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:
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:
{
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
{
name: 'build-only',
apply: 'build', // or 'serve'
}
// Function form:
{
apply(config, { command }) {
return command === 'build' && !config.build.ssr
}
}Universal Hooks (from Rolldown)
These work in both dev and build:
resolveId(id, importer)- Resolve import pathsload(id)- Load module contenttransform(code, id)- Transform module code
transform(code, id) {
if (id.endsWith('.custom')) {
return { code: compile(code), map: null }
}
}Client-Server Communication
Server to client:
configureServer(server) {
server.ws.send('my:event', { msg: 'hello' })
}Client side:
if (import.meta.hot) {
import.meta.hot.on('my:event', (data) => {
console.log(data.msg)
})
}Client to server:
// 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
-->
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:
export default defineConfig({
build: { sourcemap: false },
optimizeDeps: { include: ['lib'] },
})Multiple Environments
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
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:
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.
Backward Compatibility
server.moduleGraphreturns mixed client/SSR viewssrLoadModulestill 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:
{
name: 'env-aware',
transform(code, id, options) {
if (options?.ssr) {
// SSR-specific transform
}
},
}<!-- Source references:
- https://vite.dev/guide/api-environment
- https://vite.dev/blog/announcing-vite6
-->
Rolldown Migration (Vite 8)
Vite 8 replaces esbuild+Rollup with Rolldown, a unified Rust-based bundler.
What Changed
| Before (Vite 7) | After (Vite 8) |
|---|---|
| esbuild (dev transform) | Oxc Transformer |
| esbuild (dep pre-bundling) | Rolldown |
| Rollup (production build) | Rolldown |
rollupOptions | rolldownOptions |
esbuild option | oxc option |
Performance Impact
- 10-30x faster than Rollup for production builds
- Matches esbuild's dev performance
- Unified behavior between dev and build
Config Migration
rollupOptions → rolldownOptions
// Before (Vite 7)
export default defineConfig({
build: {
rollupOptions: {
external: ['vue'],
output: { globals: { vue: 'Vue' } },
},
},
})
// After (Vite 8)
export default defineConfig({
build: {
rolldownOptions: {
external: ['vue'],
output: { globals: { vue: 'Vue' } },
},
},
})esbuild → oxc
// Before (Vite 7)
export default defineConfig({
esbuild: {
jsxFactory: 'h',
jsxFragment: 'Fragment',
},
})
// After (Vite 8)
export default defineConfig({
oxc: {
jsx: {
runtime: 'classic',
pragma: 'h',
pragmaFrag: 'Fragment',
},
},
})JSX Configuration
export default defineConfig({
oxc: {
jsx: {
runtime: 'automatic', // or 'classic'
importSource: 'react', // for automatic runtime
},
jsxInject: `import React from 'react'`, // auto-inject
},
})Custom Transform Targets
export default defineConfig({
oxc: {
include: ['**/*.ts', '**/*.tsx'],
exclude: ['node_modules/**'],
},
})Plugin Compatibility
Most Vite plugins work unchanged. Rolldown supports Rollup's plugin API.
If a plugin only works during build:
{
...rollupPlugin(),
enforce: 'post',
apply: 'build',
}New Capabilities
Rolldown unlocks features not possible before:
- Full bundle mode (experimental)
- Module-level persistent cache
- More flexible chunk splitting
- Module Federation support
Gradual Migration
For large projects, migrate via rolldown-vite first:
# Step 1: Test with rolldown-vite
pnpm add -D rolldown-vite
# Replace vite import in config
import { defineConfig } from 'rolldown-vite'
# Step 2: Once stable, upgrade to Vite 8
pnpm add -D vite@8Overriding Vite in Frameworks
When framework depends on older Vite:
{
"pnpm": {
"overrides": {
"vite": "8.0.0"
}
}
}<!-- Source references:
- https://vite.dev/blog/announcing-vite8-beta
- https://vite.dev/blog/announcing-vite7
- https://vite.dev/config/shared-options#oxc
-->
Related skills
Forks & variants (3)
Vite has 3 known copies in the catalog totaling 2.1k installs. They canonicalize to this original listing.
How it compares
Use the vite skill for Vite-specific config and SSR; use webpack or turborepo skills when the repo does not use Vite.
FAQ
What Vite version does the vite skill target?
The vite skill version 2026.1.31 is generated from Vite 8 beta documentation and covers Rolldown-powered builds, Oxc transformer defaults, and migration notes in references/rolldown-migration.md.
What topics does the vite skill cover?
The vite skill spans vite.config.ts setup, import.meta.glob and HMR APIs, Vite plugin hooks, library mode and SSR builds, the Vite 6+ Environment API, and Rolldown migration guidance across six reference files.
When should developers use the vite skill?
Developers should load the vite skill when editing vite.config.ts, adding Vite plugins, configuring library or SSR outputs, or migrating an existing Vite 7 project to Vite 8 Rolldown defaults.
Is Vite safe to install?
skills.sh reports 3 of 3 security scanners passed. Review the Security Audits panel on this page before installing in production.