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