Vite

Intro


    npm install -g vite
    pnpm install -g vite
  

Scaffolding


    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

Root

Dependencies


    // --- MONOREPO, linked dep exported as ESM, restart after change
    export default defineConfig({
      optimizeDeps: {
        include: ['linked-dep'],
      },
      build: {
        commonjsOptions: {
          include: [/linked-dep/, /node_modules/],
        },
      },
    })
  

TypeScript


    // --- 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

JSX


    // --- 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


    // --- 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


    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
    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


    // --- 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


    // --- 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

Env Variables and Modes


    // --- .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
CommandNODE_ENVMode
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
Commandimport.meta.env.PRODimport.meta.env.DEV
NODE_ENV=productiontruefalse
NODE_ENV=developmentfalsetrue
NODE_ENV=otherfalsetrue
Commandimport.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


    // --- 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


    // --- 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

Performance

Config


    // --- 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