Getting Started with Vite
· 6 min read · Updated April 20, 2026 · intermediate
javascript vite build-tool frontend tooling
Overview
Vite is a frontend build tool that ships fast. Its dev server serves files over native ES modules — no bundling required during development. When you build for production, Vite uses Rollup to bundle your code. The result is a dev experience measured in milliseconds for hot module replacement, and optimized bundles for deployment.
Vite works with plain JavaScript, TypeScript, JSX, CSS preprocessors, and most popular frameworks out of the box. If you are coming from webpack, the mental model shift is significant: Vite does not bundle your code during development, which means your dev server startup and hot updates stay fast regardless of project size.
Project Setup
Scaffolding a new project
npm create vite@latest my-project
cd my-project
npm install
npm run dev
```bash
This scaffolds a minimal project with the correct structure. Choose your framework at the prompt — Vite has official templates for vanilla JS, React, Vue, Svelte, and more.
### Manual installation
If you already have a project and want to add Vite:
```bash
npm install -D vite
```bash
Add these scripts to `package.json`:
```json
{
"scripts": {
"dev": "vite",
"build": "vite build",
"preview": "vite preview"
}
}
```bash
`npm run dev` starts the dev server. `npm run build` creates the production bundle. `npm run preview` serves the production build locally so you can verify it before deploying.
## The Dev Server
The dev server starts fast because Vite does not bundle your code. Instead, it serves your source files as native ES modules. When the browser requests a module, Vite transforms and serves it on demand.
```javascript
// vite.config.js
import { defineConfig } from 'vite';
export default defineConfig({
server: {
port: 3000,
open: true, // open browser on start
},
});
```bash
Run `npm run dev` and visit `http://localhost:3000`.
### Hot Module Replacement
Vite's HMR is instant. When you edit a file, Vite only recompiles that specific module and sends the update to the browser. The browser applies the change without a full page reload. For React, Vue, and Svelte, this means your component state is preserved across edits.
```javascript
// vite.config.js
export default defineConfig({
server: {
hmr: {
overlay: true, // show errors in an overlay, not the console
},
},
});
```bash
### Proxy configuration
During development, proxy API requests to avoid CORS issues:
```javascript
export default defineConfig({
server: {
proxy: {
'/api': {
target: 'http://localhost:8080',
changeOrigin: true,
},
},
},
});
```bash
Requests to `/api/users` in the browser get forwarded to `http://localhost:8080/api/users`.
## Configuration
All configuration lives in `vite.config.js` (or `.ts` if using the TypeScript config format). `defineConfig()` is a helper that gives you TypeScript autocomplete:
```javascript
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
resolve: {
alias: {
'@': '/src',
},
},
build: {
outDir: 'dist',
sourcemap: true,
},
});
```bash
## Build Output
### Production bundle
```bash
npm run build
```bash
Vite uses Rollup under the hood. It produces a highly optimized bundle with:
- Code splitting by route or dynamic import
- Tree shaking of unused code
- CSS extraction and minification
- Asset hashing for cache busting
The output goes to `dist/` by default.
### Preview the production build
```bash
npm run preview
```bash
This serves the `dist/` folder locally. The preview uses the same production bundle that would be deployed — no dev server quirks slipping through.
## Working with TypeScript
Vite handles TypeScript without a separate compiler step. It uses `esbuild` to transpile TypeScript to JavaScript, which is significantly faster than `tsc`. Type checking still requires running `tsc` separately in a CI step:
```bash
npm install -D typescript
npx tsc --noEmit
```bash
Vite does not perform type checking during the build — it only strips types. Run `tsc --noEmit` in your CI pipeline to catch type errors.
## CSS and Preprocessors
Import CSS directly in your JavaScript files:
```javascript
import './styles/main.css';
```bash
Vite handles CSS modules too. Add `?inline` to import a CSS string instead of injecting it:
```javascript
import styles from './button.css?inline';
```bash
For Sass or Less, install the preprocessor:
```bash
npm install -D sass
```bash
Then write Sass as usual — Vite compiles it automatically.
## Environment Variables
Vite exposes environment variables through `import.meta.env`:
```javascript
// .env
VITE_API_URL=https://api.example.com
VITE_VERSION=1.0.0
```bash
```javascript
// In your code
console.log(import.meta.env.VITE_API_URL); // https://api.example.com
console.log(import.meta.env.VITE_VERSION); // 1.0.0
```bash
Variables must be prefixed with `VITE_` to be exposed. Variables without this prefix are ignored.
For different environments, use environment-specific files:
```bash
.env # default
.env.development # development (npm run dev)
.env.production # production (npm run build)
.env.local # local overrides, git-ignored
```bash
## Plugins
Vite's plugin API is compatible with Rollup's. Many Rollup plugins work directly in Vite.
### Official React plugin
```bash
npm install -D @vitejs/plugin-react
```bash
```javascript
// vite.config.js
import { defineConfig } from 'vite';
import react from '@vitejs/plugin-react';
export default defineConfig({
plugins: [react()],
});
```bash
This plugin enables Fast Refresh for React components — edits to components update without losing state.
### Official Vue plugin
```bash
npm install -D @vitejs/plugin-vue
```bash
```javascript
import vue from '@vitejs/plugin-vue';
export default defineConfig({
plugins: [vue()],
});
```bash
### Other common plugins
```bash
npm install -D @vitejs/plugin-legacy # ES5 fallback for old browsers
npm install -D vite-plugin-pwa # PWA support
npm install -D vite-plugin-solid # Solid.js support
```bash
## Code Splitting and Lazy Loading
Vite handles code splitting automatically when you use dynamic `import()`:
```javascript
// Instead of:
import { heavyFunction } from './heavy.js';
// Use:
const heavy = await import('./heavy.js');
heavy.heavyFunction();
```bash
The module is loaded only when `heavy.heavyFunction()` is actually called. Vite splits this into a separate chunk automatically.
For React, this pairs well with `React.lazy()`:
```javascript
import { lazy, Suspense } from 'react';
const Dashboard = lazy(() => import('./Dashboard.jsx'));
function App() {
return (
<Suspense fallback={<div>Loading...</div>}>
<Dashboard />
</Suspense>
);
}
```bash
## Common Gotchas
**Dev server vs production bundle differences.** Vite does not bundle in dev, which means some patterns behave differently. If a library works in dev but breaks in production, it may be relying on bundler-specific behavior. Use `vite-plugin-commonjs` or report the issue to the library maintainer.
**Environment variables are baked in at build time.** Changing `.env` values after `vite build` has no effect — the values are embedded in the bundle. For runtime config, embed values in the HTML file or load them from a separate endpoint.
**The `?url` suffix for raw assets.** To import an asset as a URL string:
```javascript
import myFile from './file.txt?url';
// myFile is now a string like /assets/file.abc123.txt
```bash
Without `?url`, Vite processes the file as a module.
**Monorepo setups need workspace awareness.** If your project spans multiple packages, you may need to configure `optimizeDeps.include` to pre-bundle dependencies that are imported across packages:
```javascript
export default defineConfig({
optimizeDeps: {
include: ['some-shared-lib'],
},
});
```bash
## See Also
- [/guides/javascript-modules-esm/](/guides/javascript-modules-esm/) — ESM syntax that Vite uses natively in the dev server
- [/guides/javascript-node-best-practices/](/guides/javascript-node-best-practices/) — Node.js tooling conventions that pair well with Vite
- [/tutorials/testing-with-vitest/](/tutorials/testing-with-vitest/) — test your Vite project with the Vite-native test runner