Vite
Intro
- Vite - build tool
- improves the dev server start time by dividing modules in an application into two categories:
- dependencies (unfrequently changing JS: modules, libraries) pre-bundled using
esbuild
, requests to them are strongly cached via Cache-Control: max-age=31536000,immutable
HTTP header
- source code (JSX, CSS or components) served over native ESM, requests are made conditional via
304 Not Modified
HTTP header
- Vite dev server only transforms files as requested by the browser, applies transformations only for used files
- Hot Module Replacement (HMR, replacing a module without affecting the rest of the page) on updates is performed over native ESM, chain between the edited module and its closest HMR boundary (most of the time only the module itself) is invalidated
- bundle code with a pre-configured
build
command for tree-shaking, lazy-loading and common chunk splitting (for better caching)
- during development, Vite sets
esnext
as the transform target
- for the production build, by default Vite targets browsers that support native ES Modules, native ESM dynamic import, and
import.meta
- https://github.com/vitejs/awesome-vite
npm install -g vite
pnpm install -g vite
Scaffolding
- supported templates:
vanilla
, vanilla-ts
vue
, vue-ts
react
, react-ts
, react-swc
, react-swc-ts
preact
, preact-ts
lit
, lit-ts
svelte
, svelte-ts
solid
, solid-ts
qwik
, qwik-ts
- community maintained templates
- template at
https://github.com/user/project
can be tried online using https://github.stackblitz.com/user/project
(adding .stackblitz after github to the URL of the project)
- or using degit, when project is on GitHub and uses main as the default branch
- try Vite online on StackBlitz, navigate to
vite.new/{template}
to select which framework to use
npm create vite@latest
pnpm create vite
yarn create vite
bun create vite
// --- specifying name and templates, instead of following prompts
npm create vite@latest my-vue-app -- --template vue // npm 7+, extra double-dash is needed
pnpm create vite my-vue-app --template vue
yarn create vite my-vue-app --template vue
bun create vite my-vue-app --template vue
// --- scafold a gihub user/project using degit,
// when "main" is used as main branch:
npx degit user/project#main my-project
cd my-project
npm install
npm run dev
CLI
- use the
vite
binary to run projects npm scripts: vite dev
, vite serve
, vite preview
- run vite without insalling it with
npx vite
- specify an alternative root with
vite serve some/sub/dir
, move config file (i.e. vite.config.js
) inside the project root when is changed
- COMMANDS:
vite {root}
- start dev server, aliases: vite dev
, vite serve
, options:
--host {string}
- specify hostname
--port {number}
- specify port
--strictPort
- exit if specified port is already in use
--open {boolean|string}
- open browser on startup
--cors
- enable CORS
--force
- force the optimizer to ignore the cache and re-bundle
-c {string}
, --config {string}
- use specified config file
--base {string}
- public base path (default: /
)
-l {level}
, --logLevel {level}
- info | warn | error | silent
--clearScreen
- allow/disable clear screen when logging
--profile
- start built-in Node.js inspector
-d {string|boolean}
, --debug {string|boolean}
- show debug logs
-f {string}
, --filter {string}
- filter debug logs
-m {string}
, --mode {string}
- set env mode
-h
, --help
- display this message
-v
, --version
- display version number
vite build {root}
- build for production , options:
--target {type}
- transpile target (default: "modules")
--outDir {dir}
- output directory (default: "dist")
--assetsDir {sting}
- directory under outDir to place assets in (default: "assets")
--assetsInlineLimit {number}
- static asset base64 inline threshold in bytes (default: 4096)
--ssr {string}
- build specified entry for server-side rendering
--sourcemap {boolean|inline|hidden}
- output source maps for build
--minify {boolean|terser|esbuild}
- enable/disable minification, or specify minifier to use (default: "esbuild")
--manifest {boolean|string}
- emit build manifest json
--ssrManifest {boolean|string}
- emit ssr manifest json
--emptyOutDir
- force empty outDir when it is outside of root
-w
, --watch
- rebuilds when modules have changed on disk
-c
, --config
- use specified config file
--base {string}
- public base path (default: /
)
-l {level}
, --logLevel {level}
- info | warn | error | silent
--clearScreen
- allow/disable clear screen when logging
--profile
- start built-in Node.js inspector
-d {string|boolean}
, --debug {string|boolean}
- show debug logs
-f {string}
, --filter {string}
- filter debug logs
-m {string}
, --mode {string}
- set env mode
-h
, --help
- display this message
vite optimize {root}
- pre-bundle dependencies , options:
--force
- force the optimizer to ignore the cache and re-bundle
-c
, --config
- use specified config file
--base {string}
- public base path (default: /
)
-l {level}
, --logLevel {level}
- info | warn | error | silent
--clearScreen
- allow/disable clear screen when logging
-d {string|boolean}
, --debug {string|boolean}
- show debug logs
-f {string}
, --filter {string}
- filter debug logs
-m {string}
, --mode {string}
- set env mode
-h
, --help
- display this message
vite preview {root}
- locally preview produc - CSStion build, not designed for a production server, options:
--host {string}
- specify hostname
--port {number}
- specify port
--strictPort
- exit if specified port is already in use
--open {boolean|string}
- open browser on startup
--outDir {dir}
- output directory (default: "dist")
-c {string}
, --config {string}
- use specified config file
--base {string}
- public base path (default: /
)
-l {level}
, --logLevel {level}
- info | warn | error | silent
--clearScreen
- allow/disable clear screen when logging
-d {string|boolean}
, --debug {string|boolean}
- show debug logs
-f {string}
, --filter {string}
- filter debug logs
-m {string}
, --mode {string}
- set env mode
-h
, --help
- display this message
Root
- Vite project "root directory" is referenced as
[root]
throughout the docs
index.html
is entry point to application and front-and-central (instead of being inside public
folder), treated as source code and part of the module graph, <script type="module">
and CSS referenced via <link href>
are covered by Vite-specific features
- URLs inside
index.html
are automatically rebased, no need for special %PUBLIC_URL%
placeholders
- absolute URLs in source code will be resolved using the project root as base, write code as with a normal static file server
- dependencies located out-of-root file system are supported and are usable in a monorepo-based setup
Dependencies
- Vite detects bare module imports in all served source files, and:
- PRE-BUNDLES them in development mode with esbuild, in production builds
@rollup/plugin-commonjs
is used
- converts CommonJS/UMD modules to ESM
- converts ESM dependencies with many internal modules into a single module
- after server start dependency imports are automatically discovered when not found in cache
- file system cache, pre-bundled dependencies are cached in
node_modules/.vite
- pre-bundling step will only need to be re-run when one of the following has changed:
- package manager lockfile content, e.g.
package-lock.json
, yarn.lock
, pnpm-lock.yaml
or bun.lockb
- patches folder modification time
- relevant fields in
vite.config.js
, if present
NODE_ENV
value
- force Vite to re-bundle depsby starting start the dev server with the
--force
command line option, or manually delete the node_modules/.vite
cache directory
- browser cache
- resolved dependency requests are strongly cached with HTTP headers
max-age=31536000,immutable
, invalidated by the appended version query
- disable cache in Dev tools, restart Vite with
--force
flag or reload page to debuf dependencies
- monorepo setup
- dependencies that are not resolved from
node_modules
are treated as linked dep (required to be exported as ESM), they remain unbundled, dependency list is analyzed instead
- to explicitly include/exclude dependencies from the list, use the optimizeDeps config option:
- include large and CommonJS dependencies
- do not include small and ESM dependencies
- REWRITES the imports to valid URLs like
/node_modules/.vite/deps/my-dep.js?v=f3sf2e
so that the browser can import them properly
// --- MONOREPO, linked dep exported as ESM, restart after change
export default defineConfig({
optimizeDeps: {
include: ['linked-dep'],
},
build: {
commonjsOptions: {
include: [/linked-dep/, /node_modules/],
},
},
})
TypeScript
.ts
files are supported out of the box
- Vite only performs transpilation on .ts files and does NOT perform type checking
- for production builds run
tsc --noEmit
in addition to build
command
- for more than IDE hints during development run
tsc --noEmit --watch
in a separate process, or vite-plugin-checker
for type errors directly reported in the browser
- avoid potential problems like type-only imports being incorrectly bundled Type-Only Imports and Export syntax:
import type { T } from 'only/types'
, export type { T }
// --- tsconfig.json
{
"compilerOptions": {
// not used by default, use "esbuild" > "target" config option in development
// and "build" > "target" config option in production
"target": "ES2020",
"useDefineForClassFields": true, // true, if the TS target is ESNext or ES2022 or newer, and supported by library
"lib": ["ES2020", "DOM", "DOM.Iterable"],
"module": "ESNext",
// temporarily suppress the errors with libraries (e.g. vue) that do not work well with "isolatedModules, until it is fixed upstream
"skipLibCheck": true,
// --- Bundler mode
"moduleResolution": "bundler",
"allowImportingTsExtensions": true,
"resolveJsonModule": true,
"isolatedModules": true, // should be set to true
"noEmit": true,
"jsx": "react-jsx",
// --- Linting
"strict": true,
"noUnusedLocals": true,
"noUnusedParameters": true,
"noFallthroughCasesInSwitch": true
// --- Other Compiler Options Affecting the Build Result
// extends
// importsNotUsedAsValues
// preserveValueImports
// verbatimModuleSyntax
// jsx
// jsxFactory
// jsxFragmentFactory
// jsxImportSource
// experimentalDecorators
// alwaysStrict
// shim the environment of client side code in a Vite app
"types": ["vite/client"]
// or add "vite-env.d.ts" file with: /// <reference types="vite/client" />
// this provides:
// - asset imports (e.g. importing an .svg file)
// - types for the Vite-injected env variables on import.meta.env
// - types for the HMR API on import.meta.hot
},
"include": ["src"],
"references": [{ "path": "./tsconfig.node.json" }]
}
// --- to override default typing
// custom typing file, vite-env-override.d.ts
declare module '*.svg' {
const content: React.FC<React.SVGProps<SVGElement>>
export default content
}
// file with refence vite/client:
/// <reference types="./vite-env-override.d.ts" />
/// <reference types="vite/client" />
Vue
- Vite provides first-class Vue support:
JSX
.jsx
and .tsx
files are supported out of the box, JSX transpilation is handled via esbuild
, Vue users should use the official @vitejs/plugin-vue-jsx
plugin
// --- vite.config.js
import { defineConfig } from 'vite'
import react from '@vitejs/plugin-react-swc'
export default defineConfig({
plugins: [...],
esbuild: {
// use jsxInject to avoid manual imports (which is a Vite-only option)
jsxInject: `import React from 'react'`,
// configure custom jsxFactory and jsxFragment if not using JSX with React or Vue
jsxFactory: 'h',
jsxFragment: 'Fragment',
},
})
CSS
- importing
.css
files will inject its content to the page via a <style> tag with HMR support
// --- CSS @import inlining in CSS files is supported via postcss-import:
@import url("foo-2.css");
// --- valid PostCSS config (any format supported by postcss-load-config, e.g. postcss.config.js),
// will be automatically applied to all imported CSS,
// minification will run after PostCSS and will use "build" > "cssTarget" config option
// --- any CSS file ending with .module.css is considered a CSS modules file
// mporting such a file will return the corresponding module object, example.module.css :
.red {
color: red;
}
// in component:
import classes from './example.module.css'
document.getElementById('foo').className = classes.red
// configured via "css" > "modules" config option
// with "css" > "modules" > localsConvention:'camelCaseOnly' also use named imports: .apply-color -> applyColor
import { applyColor } from './example.module.css'
document.getElementById('foo').className = applyColor
// --- it is recommended to use native CSS variables
// with PostCSS plugins that implement CSSWG drafts (e.g. postcss-nesting)
// and author plain, future-standards-compliant CSS.
// Vite does provide built-in support for .scss, .sass, .less, .styl and .stylus files
// corresponding pre-processor itself must be installed
npm add -D sass // .scss and .sass
npm add -D less // .less
npm add -D stylus // .styl and .stylus
// use CSS modules combined with pre-processors by prepending .module to the file extension,
// for example style.module.scss
// --- turn off automatic injection via the ?inline query parameter
// processed CSS string is returned as the module default export as usual
import './foo.css' // will be injected into the page
import otherStyles from './bar.css?inline' // will not be injected
// default and named imports from CSS files (e.g import style from './foo.css')
// are removed since Vite 5, use the ?inline query instead
// --- opt in for experimental support for Lightning CSS (instead of PostCSS)
// "css" > transformer:'lightningcss' config option
// and install the optional lightningcss dependency
npm add -D lightningcss
// then pass pass config option to the "css" > "lightningcss"
// configure CSS Modules with "css" > "lightningcss" > "cssModules"
// change minifier "build" > cssMinify:'lightningcss' config option, instead of esbuild.
// CSS Pre-processors are not supported when using Lightning CSS.
Assets
- importing a static asset will return the resolved public URL when it is served
- import can be either using absolute public paths (based on project root during dev) or relative paths
- common image, media, and font filetypes are detected as assets automatically, extend the internal list using the
assetsInclude
config option: assetsInclude: ['**/*.gltf']
- assets smaller in bytes than the "build" > "assetsInlineLimit" option will be inlined as base64 data URLs (default: 4096 (4 KiB))
- TypeScript, by default, does not recognize static asset imports as valid modules, to fix, include
vite-env.d.ts
file with: /// <reference types="vite/client" />
- Git LFS placeholders are automatically excluded from inlining, they do not contain the content of the file they represent, to get inlining, download the file contents via Git LFS before building
- use special
public
directory under project root
- files there will be served at root path
/
during dev, and copied to the root of the dist
directory as-is, for files that:
- never referenced in source code (e.g. robots.txt)
- must retain the exact same file name (without hashing)
- don't want to have to import an asset first just to get its URL
- public directory defaults to
[root]/public
, but can be configured via the "publicDir" config option
- always reference
public
assets using root absolute path - for example: public/icon.png
should be referenced in source code as /icon.png
import imgUrl from './img.png'
document.getElementById('hero-img').src = imgUrl // will become /assets/img.***.png in the production build
// --- explicitly load assets as URL
import assetAsURL from './asset.js?url'
// also for assets that are not included in the internal list or in "assetsInclude"
// for example, to import Houdini Paint Worklets
// --- load assets as strings
import assetAsString from './shader.glsl?raw'
// --- importing script as a Worker,
// separate chunk in the production build:
import Worker from './shader.js?worker'
const worker = new Worker()
// sharedworker:
import SharedWorker from './shader.js?sharedworker'
const sharedWorker = new SharedWorker()>
// inlined as base64 strings:
import InlineWorker from './shader.js?worker&inline'
// --- new URL(url, import.meta>.url)
// import.meta.url is a native ESM feature that exposes the current module URL
// combine it with the native URL constructor to obtain the full,
// resolved URL of a static asset using relative path from a JavaScript module:
const imgUrl = new URL('./img.png', import.meta.url).href
document.getElementById('hero-img').src = imgUrl
// dynamic URLs via template literals:
function getImageUrl(name) {
return new URL(`./dir/${name}.png`, import.meta.url).href
}
// URL string must be static so it can be analyzed
// Vite will not transform this
const imgUrl = new URL(imagePath, import.meta.url).href
// does not work with SSR
JSON
// JSON files can be directly imported, named imports are also supported,
// import the entire object
import json from './example.json'
// import a root field as named exports (helps with tree-shaking)
import { field } from './example.json'
Imports
- Glob Import - import multiple modules from the file system via the special
import.meta.glob
function
- treated like import specifiers: must be either relative ( start with
./
) or absolute ( start with /
, resolved relative to project root) or an alias path (see "resolve" > "alias" config option)
- arguments must be passed as literals, use of variables or expressions is not possible
- Vite-only feature and is not a web or ES standard
- Dynamic Import - similar to glob import, imports with variables
// --- GLOB IMPORT
const modules = import.meta.glob('./dir/*.js')
// will produce:
const modules = {
'./dir/foo.js': () => import('./dir/foo.js'),
'./dir/bar.js': () => import('./dir/bar.js'),
}
// --- iterate over the keys of the modules object:
for (const path in modules) {
modules[path]().then((mod) => {
console.log(path, mod)
})
}
// --- matched files are by default lazy-loaded via dynamic import
// and will be split into separate chunks during build
// to import all the modules directly (e.g. relying on side-effects in these modules to be applied first),
// pass { eager: true } as the second argument:
const modules = import.meta.glob('./dir/*.js', { eager: true })
// will produce:
import * as __glob__0_0 from './dir/foo.js'
import * as __glob__0_1 from './dir/bar.js'
const modules = {
'./dir/foo.js': __glob__0_0,
'./dir/bar.js': __glob__0_1,
}
// --- multiple patterns, array of globs
const modules = import.meta.glob(['./dir/*.js', './another/*.js'])
// --- negative glob patterns
// ignore some files from the result
const modules = import.meta.glob(['./dir/*.js', '!**/bar.js'])
// will produce:
const modules = {
'./dir/foo.js': () => import('./dir/foo.js'),
}
// named imports, only import parts of the modules with the import options
const modules = import.meta.glob('./dir/*.js', { import: 'setup' })
// will produce:
const modules = {glob
'./dir/foo.js': () => import('./dir/foo.js').then((m) => m.setup),
'./dir/bar.js': () => import('./dir/bar.js').then((m) => m.setup),
}
// combine with eager to have tree-shaking enabled for those modules
const modules = import.meta.glob('./dir/*.js', {
import: 'setup',
eager: true,
})
// will produce:
import { setup as __glob__0_0 } from './dir/foo.js'
import { setup as __glob__0_1 } from './dir/bar.js'
const modules = {
'./dir/foo.js': __glob__0_0,
'./dir/bar.js': __glob__0_1,
}
// import the default export
const modules = import.meta.glob('./dir/*.js', {
import: 'default',
eager: true,
})
// will produce:
import __glob__0_0 from './dir/foo.js'
// --- DYNAMIC IMPORT
const module = await import(`./dir/${file}.js`)
// variables only represent file names one level deep
// 'foo/bar' will fail, use glob instead
WebAssembly
- pre-compiled
.wasm
files can be imported with ?init
- in the production build they treated as a static asset and fetched on-demand, otherwise files smaller than
assetInlineLimit
will be inlined as base64 string
// --- default export will be an initialization function that returns a Promise of the WebAssembly.Instance:
import init from './example.wasm?init'
init().then((instance) => {
instance.exports.test()
})
// importObject as second argument:
init({
imports: {
someFunc: () => {
// ...
},
},
}).then(() => {
// ...
})
// --- to access to the Module object, e.g. to instantiate it multiple times,
// use an explicit URL import (?url) to resolve the asset, and then perform the instantiation:
import wasmUrl from 'foo.wasm?url'
const main = async () => {
const responsePromise = fetch(wasmUrl)
const { module, instance } =
await WebAssembly.instantiateStreaming(responsePromise)
// ...
}
main()
Web Workers
// --- import using new Worker() and new SharedWorker()
// compared to the worker suffixes, closer to the standards and is the recommended way to create workers.
// will only work if the new URL() constructor is used.
// all options parameters must be static values (i.e. string literals).
const worker = new Worker(new URL('./worker.js', import.meta.url))
// "module" workers options:
const worker = new Worker(new URL('./worker.js', import.meta.url), {
type: 'module',
})
// --- import with query suffixes
// direct import by appending ?worker or ?sharedworker to the import request
// default export will be a custom worker constructor
import MyWorker from './worker?worker'
const worker = new MyWorker()
// by default, the worker script will be emitted as a separate chunk in the production build.
// inline the worker as base64 strings, add the inline query
import MyWorker from './worker?worker&inline'
// retrieve the worker as a URL, add the url query:
import MyWorker from './worker?worker&url'
See Worker Options for details on configuring the bundling of all workers
// worker script can also use ESM "import" statements instead of importScripts()
// during development this relies on browser native support, for the production build it is compiled away
Production
vite build
, uses [root]/index.html
as the build entry point, produces an application bundle that is suitable to be served over a static hosting service
- build optimizations automatically applied as part of the build process:
- >CSS used by modules is automatically extracted in an async chunk and a separate file is generated for it, CSS file is automatically loaded via a
<link>
tag when the associated async chunk is loaded, and the async chunk is guaranteed to only be evaluated after the CSS is loaded to avoid FOUC (Flash Of Unstyled Content), to have all the CSS extracted into a single file, disable CSS code splitting: set "build" > "cssCodeSplit" config option to false
<link rel="modulepreload">
directive is generated for entry chunks and their direct imports in the built HTML
- "common" chunks (code that is shared between two or more other chunks) are fetched in parallel, code-split dynamic import calls are automatically rewritten, additional roundtrips are completely eliminated regardless o--- f import depth
- build can be customized via various "build" config options in
vite.config.js
- Vite targets modern JS and browsers which support the native ES Modules, native ESM dynamic import, and import.meta, specify custom targets via the "build" > "target" config option, where the lowest target is
es2015
- only syntax transforms is handled and polyfills are not covered, check https://polyfill.io/
- legacy browsers can be supported via @vitejs/plugin-legacy, will automatically generate legacy chunks and corresponding ES language feature polyfills, legacy chunks are conditionally loaded only in browsers that do not have native ESM support
- for project under a nested public path specify the "base" config option, also by specifying as a command line flag:
vite build --base=/my/public/path/
, JS-imported asset URLs, CSS url() references, and asset references in .html files are all automatically adjusted to respect this option during build
- to dynamically concatenate URLs on the fly use the globally injected
import.meta.env.BASE_URL
variable which will be the public base path, this variable is statically replaced during build, it must appear exactly as-is (i.e. import.meta.env['BASE_URL'] won't work)
- adjust directly the underlying Rollup options via "build" > "rollupOptions" (https://rollupjs.org/configuration-options/)
- configure chunks split strategy using "build" > "rollupOptions" > "output" > "manualChunks"
- use older Split Vendor Chunk strategy ( "index" and "vendor" chunks, good for some SPA) by adding the splitVendorChunkPlugin in config file
- enable rollup watcher (changes to the vite.config.js, and any files to be bundled) with
vite build --watch
, or directly adjust the underlying "WatcherOptions" via "build" > "watch"
- for multi-page app specify multiple .html files as entry points
__dirname
is always the folder of vite.config.js
file when resolving the Rollup config input paths, add root
entry to the arguments for resolve
function
- for HTML files, Vite ignores the name given to the entry in the "rollupOptions" > "input" object and instead respects the resolved id of the file when generating the HTML asset in the dist folder. This ensures a consistent structure with the way the dev server works
- Vite emits
vite:preloadError
event when it fails to load dynamic imports.code
contains the original import error
- the error will not be thrown when
event.preventDefault()
is called
// --- customizing the build, vite.config.js
import { splitVendorChunkPlugin } from 'vite'
export default defineConfig({
build: {
rollupOptions: {
// https://rollupjs.org/configuration-options/
// ...
// multi-page app - specify multiple .html files as entry points
input: {
main: resolve(__dirname, 'index.html'),
nested: resolve(__dirname, 'nested/index.html'),
},
},
watch: { // directly adjust the underlying Rollup WatcherOptions
// https://rollupjs.org/configuration-options/#watch
},
},
// using older split vendor chunk strategy (used in pair with "build" > "rollupOptions" > "output" > "manualChunks" function)
plugins: [splitVendorChunkPlugin()],
})
// --- avoiding old chuck import, etc. or other error by reloading the page
window.addEventListener('vite:preloadError', (event) => {
window.reload() // for example, refresh the page
})
// or call event.preventDefault()
Library Mode
// use index.html browser-oriented libraries development.
// use the build.lib config option for bundle library for distribution.
// also externalize any dependencies that are not bundled into library, e.g. vue or react.
// "vite build" will use Rollup preset that is oriented towards shipping libraries
// and produces two bundle formats: es and umd (configurable via build.lib):
// vite.config.js
import { resolve } from 'path'
import { defineConfig } from 'vite'
export default defineConfig({
build: {
lib: {
// Could also be a dictionary or array of multiple entry points
entry: resolve(__dirname, 'lib/main.js'),
name: 'MyLib',
// the proper extensions will be added
fileName: 'my-lib',
},
rollupOptions: {
// make sure to externalize deps that shouldn't be bundled
// into your library
external: ['vue'],
output: {
// Provide global variables to use in the UMD build
// for externalized deps
globals: {
vue: 'Vue',
},
},import.meta.
},
},
})
// entry file would contain exports that can be imported by package users
// lib/main.js
import Foo from './Foo.vue'
import Bar from './Bar.vue'
export { Foo, Bar }
// recommended package.json for your lib:
{
"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"
}
}
}
// OR if exposing multiple entry points:
{
"name": "my-lib",
"type": "module",
"files": ["dist"],
"main": "./dist/my-lib.cjs",
"module": "./dist/my-lib.js",
"exports": {
".": {
"import": "./dist/my-lib.js",
"require": "./dist/my-lib.cjs"
},
"./secondary": {
"import": "./dist/secondary.js",
"require": "./dist/secondary.cjs"
}
}
}
// without "type": "module" in package.json Vite will generate different file extensions for Node.js compatibility:
// .js will become .mjs and .cjs will become .js .
// in library mode, all import.meta.env.* usage are statically replaced when building for production,
// process.env.* usage are not, library consumers can dynamically change it,
// if this is undesirable, use define: { 'process.env.NODE_ENV': '"production"' }
// for example to statically replace them, or use https://github.com/benmccann/esm-env for better compatibility with bundlers and runtimes
// use Rollup or esbuild directly to build non-browser libraries or for advanced build flows
Static Site
- deploy static site
- the default build output location (dist) is used or defined using "build" > "outDir" config option
- package manager is used: npm, yarn, pnpm
- Vite is installed as a local dev dependency in project, and
build
with preview
scripts are setup
- 1 -
npm run build
- build the app, output will be placed in defined directory
- 2 -
npm run preview
- boot up a local static web server that serves the files from build directory at http://localhost:4173
- configure the preview port of the server:
vite preview --port 8080
- ... TODO: deployment locations, Netlify, Vercel, Cloudflare ...
Env Variables and Modes
- special
import.meta.env
object exposes env variables (also exposed to client source code), some built-in variables are available in all cases
import.meta.env.MODE
- mode the app is running in
import.meta.env.BASE_URL
- base url the app is being served from, determined by the base config option
import.meta.env.PROD
- whether the app is running in production (running the dev server with NODE_ENV='production'
or running an app built with NODE_ENV='production'
)
import.meta.env.DEV
- whether the app is running in development (always the opposite of import.meta.env.PROD
)
import.meta.env.SSR
- whether the app is running in the server
- dotenv file is used to load additional environment variables from the following files in environment directory ("envDir" config option)
- env file for a specific mode (e.g. .env.production) will take higher priority than a generic one (e.g. .env)
- environment variables that already exist when Vite is executed (
VITE_SOME_KEY=123 vite build
) have the highest priority and will not be overwritten by .env files
- .env files are loaded at the start of Vite, restart the server after making changes
- only variables prefixed with
VITE_
are exposed to Vite-processed client source code as string: import.meta.env.VITE_*
, should not contain any sensitive information
- dotenv-expand is used to expand variables
- to get TypeScript IntelliSense for user-defined env variables that are prefixed with
VITE_
create an vite-env.d.ts
in src
directory, and augment ImportMetaEnv
- any properties in
import.meta.env
can be used in HTML files with a special %ENV_NAME%
syntax
- by default, the dev server (
dev
command ) runs in development mode and the build
command runs in production mode and loads the env variables from .env.production
- strongoverwrite the default build mode:
vite build --mode staging
(to use .env.staging file
)
- run a development build by using a different mode and .env file configuration, set
NODE_ENV=development
in .env.*
file
NODE_ENV=...
can be set in the command line and in .env
file
- if
NODE_ENV
is specified in a .env.[mode]
file, the mode can be used to control its value, however, both NODE_ENV and modes remain as two different concepts
- benefit of
NODE_ENV=...
in the command is that it allows Vite to detect the value early and allows to read process.env.NODE_ENV
in Vite config as Vite can only load the env files once the config is evaluated
.env.*.local
files are local-only and can contain sensitive variables, add *.local
to .gitignore to avoid them being checked into git
// --- .env files rules
.env // loaded in all cases
.env.local // loaded in all cases, ignored by git
.env.[mode] // only loaded in specified mode (higher priority than a generic one (e.g. .env))
.env.[mode].local // only loaded in specified mode, ignored by git(higher priority than a generic one (e.g. .env))
// --- only variables prefixed with VITE_ are exposed to client source code, .env file
VITE_SOME_KEY=123
DB_PASSWORD=foobar
// JS
console.log(import.meta.env.VITE_SOME_KEY) // "123" - returned as string, convert
console.log(import.meta.env.DB_PASSWORD) // undefined
// --- escape "$" with "\" ( because of dotenv-expand )
KEY=123
NEW_KEY1=test$foo // test
NEW_KEY2=test\$foo // test$foo
NEW_KEY3=test$KEY // test123
// --- properties in import.meta.env can be used in HTML files with a special %ENV_NAME% syntax:
<h1>Vite is running in %MODE%</h1>
<p>Using data from %VITE_API_URL%</p>
// non-existent are ignored and not replaced
NODE_ENV ( process.env.NODE_ENV ) and modes are two different concepts
Command | NODE_ENV | Mode |
vite build | "production" | "production" |
vite build --mode development | "production" | "development" |
NODE_ENV=development vite build | "development" | "production" |
NODE_ENV=development vite build --mode development | "development" | "development" |
different values of NODE_ENV and mode also reflect on its corresponding import.meta.env properties
Command | import.meta.env.PROD | import.meta.env.DEV |
NODE_ENV=production | true | false |
NODE_ENV=development | false | true |
NODE_ENV=other | false | true |
Command | import.meta.env.MODE |
--mode production | "production" |
--mode development | "development" |
--mode staging | "staging" |
// --- IntelliSense for TypeScript, augmenting ImportMetaEnv in src/vite-env.d.ts
/// <reference types="vite/client" />
interface ImportMetaEnv {
readonly VITE_APP_TITLE: string
// more env variables...
}
interface ImportMeta {
readonly env: ImportMetaEnv
}
// avoid any import statement if the ImportMetaEnv augmentation does not work.
// if code relies on types from browser environments such as DOM and WebWorker,
// update the lib field in tsconfig.json
{
"lib": ["WebWorker"]
}
Server-Side Rendering
- low-level API section meant for library and framework authors, Vite-specific integration details for SSR (running front-end frameworks (React, Preact, Vue, Svelte) application in Node.js, pre-rendering it to HTML, and finally hydrating it on the client)
- check higher-level SSR plugins and tools at Awesome Vite SSR to create an application on top of Vite native low-level API
- for integration with traditional server-side frameworks, use Backend Integration guide
- scaffold example projects with
npm create vite@latest
> "Others" > "create-vite-extra", references:
- it is recommended to use Vite in middleware mode, to decouple from the production environment
- pre-render routes that are known ahead of time into static HTML using the same logic as production SSR (a form of Static-Site Generation (SSG)), working example
- dependencies are "externalized" from SSR transform module system by default to speed up both dev and build, to transform dependency by Vite pipeline, (Vite features are used untranspiled in them,...), they can be added to "ssr" > "noExternal". Linked dependencies are not externalized by default to take advantage of HMR, to test dependencies as if they aren't linked, etc.: add it to "ssr" > "external" config option
- configure the target for the SSR build to be Web Worker using the "ssr" > "target" set to 'webworker', default target for the SSR build is a node environment, packages entry resolution is different for each platform
- bundle SSR build into a single JavaScript file (cases like webworker runtimes) by setting "ssr" > "noExternal" to "true", will do two things: treat all dependencies as "noExternal" , throw an error if any Node.js built-ins are imported
- use "ssr" > "resolve" > "conditions" and "ssr" > "resolve" > "externalConditions" config options to customize package entry resolution for the SSR build (by default conditions set in "resolve" > "conditions")
vite dev
and vite preview
can also be used for SSR apps, add SSR middlewares to the development server with "configureServer" and to the preview server with "configurePreviewServer" plugin hooks, use a post hook so that SSR middleware runs after Vite middlewares
// --- typical SSR application will have the following source file structure:
- index.html
- server.js // main application server
- src/
- main.js // exports env-agnostic (universal) app code
- entry-client.js // mounts the app to a DOM element
- entry-server.js // renders the app using the framework SSR API
// --- index.html will need to reference entry-client.js
// and include a placeholder where the server-rendered markup should be injected:
<div id="app">
<!--ssr-outlet--> // any placeholder that can be precisely replaced
</div>
<script type="module" src="/src/entry-client.js"></script>
// --- conditional logic, based on SSR vs. client
// statically replaced during build, allow tree-shaking of unused branches
if (import.meta.env.SSR) {
// ... server only logic
}
// --- USE VITE IN MIDDLEWARE MODE, decouple from the production environment
// with ExpressJS, server.js :
import fs from 'fs'
import path from 'path'
import { fileURLToPath } from 'url'
import express from 'express'
import { createServer as createViteServer } from 'vite'
const __dirname = path.dirname(fileURLToPath(import.meta.url))
async function createServer() {
const app = express()
// create Vite server in middleware mode and configure the app type as 'custom',
// disabling own HTML serving logic so parent server can take control
const vite = await createViteServer({
server: { middlewareMode: true },
appType: 'custom'
})
// using connect instance (https://github.com/senchalabs/connect) as middleware,
// can be used in any connect-compatible Node.js framework.
// with own express.Router() use router.use .
// when the server restarts (for example after the user modifies vite.config.js),
// "vite.middlewares" is still going to be the same reference
// (with a new internal stack of Vite and plugin-injected middlewares)
// following is valid even after restarts
app.use(vite.middlewares)
app.use('*', async (req, res, next) => {
const url = req.originalUrl
try {
// 1. Read index.html
let template = fs.readFileSync(
path.resolve(__dirname, 'index.html'),
'utf-8',
)
// 2. Apply Vite HTML transforms.
// inject the Vite HMR client, and apply HTML transforms from Vite plugins,
// e.g. global preambles from @vitejs/plugin-react
template = await vite.transformIndexHtml(url, template)
// 3a. Load the server entry.
// ssrLoadModule automatically transforms ESM source code to be usable in Node.js
const { render } = await vite.ssrLoadModule('/src/entry-server.js')
// 3b. Since Vite 5.1, use createViteRuntime API instead,
// fully supports HMR and works in a simillar way to ssrLoadModule.
// advanced use cases: runtime in a separate thread or a different machine using ViteRuntime class
const runtime = await vite.createViteRuntime(server)
const { render } = await runtime.executeEntrypoint('/src/entry-server.js')
// 4. Render the app HTML.
// assumes entry-server.js exported "render" function calls appropriate framework SSR APIs,
// e.g. ReactDOMServer.renderToString()
const appHtml = await render(url)
// 5. Inject the app-rendered HTML into the template.
const html = template.replace(`<!--ssr-outlet-->`, appHtml)
// 6. Send the rendered HTML back.
res.status(200).set({ 'Content-Type': 'text/html' }).end(html)
} catch (e) {
// fix the stack trace on error when caught, maps back to actual source code
vite.ssrFixStacktrace(e)
next(e)
}
})
app.listen(5173)
}
createServer()
// --- building SSR project for production.
// refer to the example projects for a working setup.
// 1 - produce a client build as normal;
// 2 - produce an SSR build, which can be directly loaded via import()
// so that we dont have to go through Vite ssrLoadModule or runtime.executeEntrypoint;
// scripts in package.json :
{
"scripts": {
"dev": "node server",
"build:client": "vite build --outDir dist/client",
"build:server": "vite build --outDir dist/server --ssr src/entry-server.js"
}
}
// in server.js add production specific logic by checking process.env.NODE_ENV:
// 1 - instead of reading the root index.html, use the dist/client/index.html as the template,
// it contains the correct asset links to the client build.
// 2 - instead of await vite.ssrLoadModule('/src/entry-server.js')
// or await runtime.executeEntrypoint('/src/entry-server.js'),
// use import('./dist/server/entry-server.js') (this file is the result of the SSR build).
// 3 - move the creation and all usage of the vite dev server behind dev-only conditional branches,
// then add static file serving middlewares to serve files from dist/client.
// --- generate dist/client/.vite/ssr-manifest.json from the client build,
// with mappings of module IDs to their associated client files chunks and assets.
// package.json :
"build:client": "vite build --outDir dist/client --ssrManifest",
// Vue example of collecting the module IDs of the components:
// https://github.com/vitejs/vite-plugin-vue/blob/main/playground/ssr-vue/src/entry-server.js
// --- SSR-specific plugin logic
// some frameworks such as Vue or Svelte
// compile components into different formats based on client vs. SSR
// to support conditional transforms,
// use "ssr" property in the options object of the following plugin hooks:
// - resolveId
// - load
// - transform
export function mySSRPlugin() {
return {
name: 'my-ssr',
transform(code, id, options) {
if (options?.ssr) {
// perform ssr-specific transform...
}
},
}
}
Backend Integration
// 1 - vite.config.js , configure the entry and enable build manifest:
export default defineConfig({
build: {
manifest: true, // generate .vite/manifest.json in outDir
rollupOptions: {
input: '/path/to/main.js', // overwrite default .html entry
},
},
})
// if module preload polyfill is not disabled, add the beginning of app entry
import 'vite/modulepreload-polyfill'
// 2 - for development,
// inject the following in server HTML template
// (substitute http://localhost:5173 with the local URL Vite is running at):
<script type="module" src="http://localhost:5173/@vite/client"></script>
<script type="module" src="http://localhost:5173/main.js"></script>
// to properly serve assets, for assets such as images to load properly
// (a) - make sure the server is configured to proxy static assets requests to the Vite server.
// (b) - set "server" > "origin" config option so that generated asset URLs
// will be resolved using the back-end server URL instead of a relative path.
// for React with @vitejs/plugin-react,
// since the plugin is not able to modify the HTML served
// (substitute http://localhost:5173 with the local URL Vite is running at)
// add before the above scripts:
<script type="module">
import RefreshRuntime from 'http://localhost:5173/@react-refresh'
RefreshRuntime.injectIntoGlobalHook(window)
window.$RefreshReg$ = () => {}
window.$RefreshSig$ = () => (type) => type
window.__vite_plugin_react_preamble_installed__ = true
</script>
// 3 - for production: after running "vite build",
// ".vite/manifest.json" file will be generated alongside other asset files
// example manifest file looks like this:
{
"main.js": {
"file": "assets/main.4889e940.js",
"src": "main.js",
"isEntry": true,
"dynamicImports": ["views/foo.js"],
"css": ["assets/main.b82dbe22.css"],
"assets": ["assets/asset.0ab0f9cd.png"]
},
"views/foo.js": {
"file": "assets/foo.869aea0d.js",
"src": "views/foo.js",
"isDynamicEntry": true,
"imports": ["_shared.83069a53.js"]
},
"_shared.83069a53.js": {
"file": "assets/shared.83069a53.js"
}
}
// - manifest has a Record<name, chunk> structure
// - for entry or dynamic entry chunks, the key is the relative src path from project root
// - for non entry chunks, the key is the base name of the generated file prefixed with _
// - chunks will contain information on its static and dynamic imports
// (both are keys that map to the corresponding chunk in the manifest),
// and also its corresponding CSS and asset files (if any)
// use this file to render links or preload directives with hashed filenames
// (note: the syntax here is for explanation only, substitute with server templating language):
<link rel="stylesheet" href="/assets/{{ manifest['main.js'].css }}" />
<script type="module" src="/assets/{{ manifest['main.js'].file }}"></script>
Plugins
- Vite can be extended using Rollup plugins, using interface with a few extra Vite-specific options, extend the dev server and SSR functionality as needed
plugins
array in vite.config.js
also accepts presets including several plugins as a single element, useful for complex features (like framework integration) that are implemented using several plugins, array will be flattened internally
- falsy plugins will be ignored, which can be used to easily activate or deactivate plugins
- https://github.com/vitejs/awesome-vite#plugins
- https://vite-rollup-plugins.patak.dev/
- npm search for rollup-plugin
// --- ADDING PLUGIN
// provide support for legacy browsers using official @vitejs/plugin-legacy.
// add to the "devDependencies" of the project
npm add -D @vitejs/plugin-legacy
// include in the plugins array in "vite.config.js"
import legacy from '@vitejs/plugin-legacy'
import image from '@rollup/plugin-image'
import { defineConfig } from 'vite'
export default defineConfig({
plugins: [
legacy({
targets: ['defaults', 'not IE 11'],
}),
{
...image(),
// enforce the order of the plugin, invoke plugin
// pre - before core plugins
// default - after core plugins
// post - after build plugins
enforce: 'pre',
// conditional application, avoid invoking by both serve and build
apply: 'build', // or 'serve'
},
],
})
Troubleshooting
- Rollup troubleshooting guide
- CJS
- Vite CJS Node API deprecated
vite.config.js
file content must use the ESM syntax
- closest
package.json
file should have "type": "module"
, or use the .mjs
/ .mts
extension, e.g. vite.config.mjs
or vite.config.mts
- configure ESM as default, opt-in to CJS if needed: add
"type": "module"
in the project package.json
, all *.js
files are now interpreted as ESM and needs to use the ESM syntax, rename a file with the .cjs
extension to keep using CJS instead
- keep CJS as default, opt-in to ESM if needed: if the project
package.json
does not have "type": "module"
, all *.js
files are interpreted as CJS, rename a file with the .mjs
extension to use ESM instead
- dynamically import Vite:, to need to keep using CJS, dynamically import Vite using
import('vite')
instead, this requires code to be written in an async
context, but should still be manageable as Vite's API is mostly asynchronous
- to see where thewarning is coming from, log the stack trace:
VITE_CJS_TRACE=true vite dev
- to temporarily ignore the warning:
VITE_CJS_IGNORE_WARNING=true vite dev
- postcss config files does not support ESM + TypeScript (
.mts
or .ts
in "type": "module"
) yet, with postcss configs with .ts
and added "type": "module"
to package.json, rename the postcss config to use .cts
- CLI
Error: Cannot find module 'C:\foo\bar&baz\vite\bin\vite.js'
- path to project folder must not include
&
, does not work with npm
on Windows ( npm/cmd-shim#45 ), switch to another package manager (e.g. pnpm, yarn), OR remove &
from the path to project
- Config
This package is ESM only
, ESM files cannot be loaded by require
- adding
"type": "module"
to the nearest package.json
- renaming
vite.config.js
/ vite.config.ts
to vite.config.mjs
/ vite.config.mts
- Dev Server
- Requests are stalled forever, file descriptor limits and inotify limits may be causing the issue in Linux environment, Vite does not bundle most of the files, browsers may request many files which require many file descriptors, going over the limit
- increase file descriptor limit by
ulimit
: check current limit - ulimit -Sn
, change limit (temporary) - ulimit -Sn 10000
, restart browser, try to change the hard limit too
- increase the following inotify related limits by
sysctl
: check current limits - sysctl fs.inotify
, change limits (temporary) - sudo sysctl fs.inotify.max_queued_events=16384
, sudo sysctl fs.inotify.max_user_instances=8192
, sudo sysctl fs.inotify.max_user_watches=524288
- try adding
DefaultLimitNOFILE=65536
as an un-commented config to the following files: /etc/systemd/system.conf
, /etc/systemd/user.conf
, for Ubuntu Linux, add the line * - nofile 65536
to the file /etc/security/limits.conf
instead of updating systemd config files
- these settings persist but a restart is required
Network requests stop loading
- Network requests stop loading, when using a self-signed SSL certificate, Chrome ignores all caching directives and reloads the content, Vite relies on these caching directives
- use a trusted SSL cert
- see: cache problems , Chrome issue
- on macOS, install a trusted cert via the CLI with this command:
security add-trusted-cert -d -r trustRoot -k ~/Library/Keychains/login.keychain-db your-cert.cer
, or Network requests stop loadingby importing it into the Keychain Access app and updating the trust of your cert to "Always Trust."
- 431 Request Header Fields Too Large, when the server / WebSocket server receives a large HTTP header, because Node.js limits request header size to mitigate CVE-2018-12121
- reduce request header size: if the cookie is long, delete it, etc.
- use
--max-http-header-size
to change max header size
- HMR
- Vite detects a file change but the HMR is not working
- file with a different case is imported:
src/foo.js
exists and src/bar.js
contains: import './Foo.js' // should be './foo.js'
- Vite does not detect a file change
- when running Vite with WSL2, Vite cannot watch file changes in some conditions, see "server" > "watch" option (file system watcher options to pass on to chokidar)
- A full reload happens instead of HMR, if HMR is not handled by Vite or a plugin, the only way to refresh the state , if HMR is handled but it is within a circular dependency, a full reload will also happen to recover the execution order
- run
vite --debug hmr
to log the circular dependency path if a file change triggered it, break the loop
- Build
- Built file does not work because of CORS error:
Access to script at 'file:///foo/bar.js' from origin 'null' has been blocked by CORS policy,...
- if the HTML file output was opened with file:///
protocol
- access the file with
http
protocol, run npx vite preview
- Optimized Dependencies
- Outdated pre-bundled deps when linking to a local package, hash key used to invalidate optimized dependencies depend on the package lock contents, the patches applied to dependencies, and the options in the Vite config file that affects the bundling of node modules, Vite will detect when a dependency is overridden using a feature as npm overrides , and re-bundle dependencies on the next server start, Vite wont invalidate the dependencies when feature like npm link are used
- when dependencies are linked or unlinked, force re-optimization on the next server start by using
vite --force
- use overrides insteyourad, which are supported now by every package manager (pnpm overrides , yarn resolutions)
- Performance bottlenecks
- Rollup troubleshooting guide
- start the built-in Node.js inspector
vite --profile --open
(in dev) OR vite build --profile
(for build) to catch performance bottlenecks, in browser, press p in terminal (will stop the Node.js inspector) then press q key to stop the dev server, Node.js inspector will generate vite-profile-0.cpuprofile
in the root folder, upload the CPU profile using the BROWSE button to inspect the result at https://www.speedscope.app/
- install vite-plugin-inspect
- Others
Module "fs" has been externalized for browser compatibility. Cannot access "fs.readFile" in client code
, when a Node.js module is used in the browser
- because Vite does not automatically polyfill Node.js modules
- avoid Node.js modules for browser code to reduce the bundle size
- add polyfills manually
- Syntax Error / Type Error happens,
...strict mode...
, Vite cannot handle and does not support code that only runs on non-strict mode (sloppy mode), only ESM in permanent strict mode
- Browser extensions (like ad-blockers) may prevent the Vite client from sending requests to the Vite dev server(white screen without logged errors in this case)
- Cross drive links on Windows, with cross drive links in project on Windows (virtual drive linked to a folder by
virtual drive linked to a folder by subst command
command, symlink/junction to a different drive by mklink command (e.g. Yarn global cache)), Vite may not work
Performance
- Avoid Browser Extensions - may interfere with requests and slow down startup and reload times for large apps, especially when using browser dev tools, create a dev-only profile without extensions, or switch to incognito mode, while using dev server
- Audit Configured Vite Plugins
- large dependencies that are only used in certain cases should be dynamically imported to reduce the Node.js startup time, example refactors: vite-plugin-react#212 and vite-plugin-pwa#224
buildStart
, config
, and configResolved
hooks should not run long and extensive operations, they are awaited during dev server startup, which delays accessing the site in the browser
resolveId
, load
, and transform
hooks may cause some files to load slower than others, sometimes unavoidable, it is still worth checking for possible areas to optimize, checking if the code
contains a specific keyword, or the id
matches a specific extension, before doing the full transformationThe longer it takes to transform a file, the more significant the request waterfall will be when loading the site in the browser, inspect the duration it takes to transform a file using DEBUG="vite:plugin-transform" vite
or vite-plugin-inspect, as asynchronous operations tend to provide inaccurate timings, treat the numbers as a rough estimate, but it should still reveal the more expensive operations
- run
vite --profile
, visit the site, and press p + enter in terminal to record a .cpuprofile
, tool like speedscope can then be used to inspect the profile and identify the bottlenecks, also share the profiles with the Vite team
- Reduce Resolve Operations - reduce "guessing" import paths with the "resolve" > "extensions" config option, which defaults to
['.mjs', '.js', '.mts', '.ts', '.jsx', '.tsx', '.json']
, make sure it works for files in node_modules too, for TypeScript enable "moduleResolution": "bundler"
and "allowImportingTsExtensions": true
in tsconfig.json
compilerOptions
to use .ts
and .tsx
extensions directly in code
- Avoid Barrel Files (files that re-export the APIs of other files in the same directory:
src/*/index.js
exports), they need to be fetched and transformed: slash API and side-effects that run on initialization, import the individual APIs directly, e.g. import { slash } from './utils/slash.js'
- Warm Up Frequently Used Files with "server" > "warmup" > "clientFiles"|"ssrFiles" config option fast-glob paths array (transform and cache the results in advance), using
--open
or "server" > "open" provides a performance boost, as Vite will automatically warm up the entry point, also find files that are frequently used by running DEBUG="vite:transform" vite
and inspect the logs
- Use Lesser or Native Tooling, reduce the amount of work for the source files (JS/TS/CSS)
- less work:
- use CSS instead of Sass/Less/Stylus when possible (nesting can be handled by PostCSS)
- do not transform SVGs into UI framework components (React, Vue, etc), import them as strings or URLs instead
- when using
@vitejs/plugin-react
, avoid configuring the Babel options, it skips the transformation during build (only esbuild will be used)
- native tooling, brings larger installation size, not the default when starting a new Vite project, may be worth for larger applications
Config
- Vite will automatically try to resolve a config file named
vite.config.js
inside project root (other JS and TS extensions are also supported) when running vite
from the command line
- using ES modules syntax in the config file even if the project is not using native Node ESM is supported, e.g. type: "module" in
package.json
, in this case, the config file is auto pre-processed before load
vite --config my-config.js
- specify a config file to use, resolved relative to cwd
// --- most basic config file, vite.config.js
export default {
// config options
}
// --- intellisense with jsdoc type hints:
/** @type {import('vite').UserConfig} */
export default {
// ...
}
// OR use the defineConfig helper, provides intellisense without jsdoc annotations:
import { defineConfig } from 'vite'
export default defineConfig({
// ...
})
// TS config files are also supported, use vite.config.ts with the defineConfig helper
// --- Conditional Config
export default defineConfig(({
command, // serve ( "vite", "vite dev", "vite serve" is running ) | build ( "vite build" )
mode,
isSsrBuild,
isPreview
}) => {
if (command === 'serve') {
return {
// dev specific config
}
} else {
// command === 'build'
return {
// build specific config
}
}
})
// --- Async Config, to call async functions in config, export an async function instead
// also pass through defineConfig to improve intellisense support:
export default defineConfig(async ({ command, mode }) => {
const data = await asyncFunction()
return {
// vite config
}
})
// --- Environment Variables in Config
// can be obtained with exported loadEnv helper
import { defineConfig, loadEnv } from 'vite'
export default defineConfig(({ command, mode }) => {
const env = loadEnv(
mode, // load env file based on "mode" in the current working directory
process.cwd(),
'' // '' - to load all env regardless of the `VITE_` prefix
)
return {
// vite config
define: {
__APP_ENV__: JSON.stringify(env.APP_ENV),
},
}
})
Back to Main Page