
Vite
- 1.9k installs
- 696 repo stars
- Updated July 27, 2026
- onmax/nuxt-skills
This is a copy of vite by antfu - installs and ranking accrue to the original listing.
vite is a Claude Code skill that configures Vite library builds, SSR, and multi-page apps with correct Rollup options and package.json exports for developers shipping frontend packages.
About
vite is a Claude Code skill from onmax/nuxt-skills for configuring Vite beyond default SPA setups. The skill documents library mode with defineConfig, entry resolution, fileName patterns, and rolldownOptions for externalizing Vue and React peers with globals mapping. It covers multiple library entries, SSR configuration, and package.json exports so distributable packages resolve correctly for consumers. Developers reach for vite when a Vite project needs library distribution, server-side rendering, or multi-page bundling and rollup externals or export maps are misconfigured.
- Library mode configuration with multiple entry points
- SSR and multi-page app setup examples
- Output format handling for ES, UMD, and CJS
- Package.json exports and files configuration
- Rolldown options for externals and globals
Vite by the numbers
- 1,896 all-time installs (skills.sh)
- +35 installs in the week ending Aug 5, 2026 (Skillselion tracking)
- Security screen: LOW risk (skills.sh audit)
- Data as of Aug 5, 2026 (Skillselion catalog sync)
npx skills add https://github.com/onmax/nuxt-skills --skill viteAdd your badge
Show developers this skill is listed on Skillselion. Paste this into your README.
| Installs | 1.9k |
|---|---|
| repo stars | ★ 696 |
| Security audit | 3 / 3 scanners passed |
| Last updated | July 27, 2026 |
| Repository | onmax/nuxt-skills ↗ |
How do you configure Vite library mode and SSR?
Quickly configure Vite for library builds, SSR, and multi-page apps with correct rollup options and package.json exports.
Who is it for?
Frontend developers publishing npm libraries or SSR apps who need precise Vite and Rollup configuration beyond create-vite defaults.
Skip if: Backend API work, Nuxt-specific routing beyond Vite config, or teams using Webpack or Turbopack exclusively.
When should I use this skill?
A Vite project needs library mode, SSR, multi-entry builds, or package.json export fixes.
What you get
Working vite.config.ts with lib entries, rolldownOptions externals, SSR settings, and correct package.json export maps.
- vite.config.ts library and SSR settings
- package.json export map
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
Cross-Skill References
- Testing → Use
vitestskill (Vite-native testing) - Vue projects → Use
vueskill for component patterns - Library bundling → Use
tsdownskill for TypeScript libs
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
Middleware Mode
Use Vite as middleware in a custom server:
import express from 'express'
import { createServer as createViteServer } from 'vite'
const app = express()
const vite = await createViteServer({
server: { middlewareMode: true },
appType: 'custom',
})
app.use(vite.middlewares)
app.use('*all', async (req, res, next) => {
const url = req.originalUrl
// 1. Read and transform index.html
let template = await fs.readFile('index.html', 'utf-8')
template = await vite.transformIndexHtml(url, template)
// 2. Load server entry
const { render } = await vite.ssrLoadModule('/src/entry-server.ts')
// 3. Render app
const appHtml = await render(url)
// 4. Inject into template
const html = template.replace('<!--ssr-outlet-->', appHtml)
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
})
app.listen(5173)SSR Build
{
"scripts": {
"build:client": "vite build --outDir dist/client",
"build:server": "vite build --outDir dist/server --ssr src/entry-server.ts"
}
}The --ssr flag:
- Externalizes dependencies by default
- Outputs for Node.js consumption
SSR Manifest
Generate asset mapping for preload hints:
vite build --outDir dist/client --ssrManifestCreates dist/client/.vite/ssr-manifest.json mapping module IDs to chunks.
SSR Externals
Control which deps get bundled vs externalized:
export default defineConfig({
ssr: {
noExternal: ['some-package'], // Bundle this dep
external: ['another-package'], // Externalize this dep
},
})Conditional Logic
if (import.meta.env.SSR) {
// Server-only code (tree-shaken from client)
}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/ssr
- https://vite.dev/guide/api-javascript
-->
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
How it compares
Pick vite over general frontend skills when the blocker is Vite-specific build configuration—library mode, SSR, or export maps—rather than component or styling work.
FAQ
What does the vite skill configure?
The vite skill configures Vite library mode, SSR, multi-page apps, rolldownOptions externals, and package.json exports. It provides vite.config.ts patterns for distributable packages with Vue or React peer dependencies.
When should I use vite versus default Vite templates?
Use the vite skill when library distribution, SSR, or multi-entry builds require custom Rollup externals and export maps. Default create-vite scaffolds lack the rolldownOptions and exports detail this skill supplies.
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.