
# Vite Integration

> Use Nitro as a Vite plugin to build full-stack applications, and choose the builder that bundles your server.

Nitro integrates with [Vite](https://vite.dev) as a plugin. Adding it to a Vite project gives you a full server alongside your frontend: API routes, server-side rendering, and a production build that deploys anywhere.

## Nitro as a Vite plugin

Add the plugin to your Vite config:

```ts [vite.config.ts]
import { defineConfig } from "vite";
import { nitro } from "nitro/vite";

export default defineConfig({
  plugins: [nitro()],
});
```

With the plugin enabled, you use the Vite CLI for everything:

- **`vite dev`**: One dev server for both frontend and backend. Nitro serves your server routes and assets while Vite handles client modules. Server code runs in an isolated worker and automatically reloads when server files change, while client code keeps Vite's hot module replacement (HMR).
- **`vite build`**: Builds the client and the server together into a single deployable `.output/` directory, using any [deployment preset](/deploy).
- **`vite preview`**: Serves the production build locally through Nitro, including static assets and WebSocket support.

::tip
Follow the [quick start](/docs/quick-start#add-to-a-vite-project) for a step-by-step guide to adding Nitro to an existing Vite project.
::

::note
When using the Vite plugin, prefer `vite dev` over `nitro dev`; the Nitro CLI dev server does not support the Vite builder. `nitro build` works and runs the Vite build under the hood.
::

## Frontend frameworks

The Nitro plugin composes with other Vite plugins, so you can use your favorite frontend framework for server-side rendering with client hydration:

```ts [vite.config.ts]
import { defineConfig } from "vite";
import { nitro } from "nitro/vite";
import react from "@vitejs/plugin-react";

export default defineConfig({
  plugins: [nitro(), react()],
});
```

Nitro automatically detects a file named `entry-server.(ts|js|tsx|jsx|mjs)` (in the project root, `app/`, `src/`, or the server directory) and uses it as the SSR entry.

Instead of re-explaining each setup, see the working examples:

- [SSR with React](/examples/vite-ssr-react)
- [SSR with Vue Router](/examples/vite-ssr-vue-router)
- [SSR with Solid](/examples/vite-ssr-solid)
- [SSR with Preact](/examples/vite-ssr-preact)
- [React Server Components](/examples/vite-rsc)
- [SSR with plain HTML](/examples/vite-ssr-html)

::read-more{to="/docs/renderer"}
Learn more about how unmatched routes are rendered in the [renderer](/docs/renderer) and [server entry](/docs/server-entry) guides.
::

## Configuring Nitro with Vite

Nitro configuration can live in three places, all accepting the same [options](/config):

1. A `nitro.config.ts` file (recommended for most projects)
2. The `nitro` key in your Vite config
3. Inline plugin options passed to `nitro()`

::code-group
```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  serverDir: "./server",
});
```
```ts [vite.config.ts (nitro key)]
import { defineConfig } from "vite";
import { nitro } from "nitro/vite";

export default defineConfig({
  plugins: [nitro()],
  nitro: {
    serverDir: "./server",
  },
});
```
```ts [vite.config.ts (inline)]
import { defineConfig } from "vite";
import { nitro } from "nitro/vite";

export default defineConfig({
  plugins: [
    nitro({
      serverDir: "./server",
    }),
  ],
});
```
::

Inline plugin options take precedence over the `nitro` key in the Vite config, and both override `nitro.config.ts`. The `dev` mode and `rootDir` are inferred from Vite and cannot be overridden.

Vite plugins can also extend Nitro programmatically by exposing a `nitro` key (a [Nitro module](/docs/modules)) on the plugin object. See the [Vite Nitro plugin example](/examples/vite-nitro-plugin).

### Using a specific Vite package

Nitro does not depend on `vite` itself: it resolves the one installed in your project. A framework running Vite programmatically should pass its own `vite` package with the `vite.path` option, so the dev module runner is created from the Vite instance that is actually running (in a monorepo, a different version can be hoisted next to the app):

```ts
import { createServer } from "vite";
import { nitro } from "nitro/vite";

const server = await createServer({
  plugins: [nitro({ vite: { path: import.meta.resolve("vite") } })],
});
```

The option accepts a path or `file://` URL to the `vite` package directory or entry.

### Experimental plugin options

The plugin additionally accepts Vite-specific flags under `experimental.vite`:

::field{name="assetsImport" type="boolean"}
Enable `?assets` imports for collecting CSS and JS assets from entry points (default: `true`).
::

::field{name="serverReload" type="boolean"}
Reload the server (and browser) when a server-only module changes (default: `true`).
::

::field{name="services" type="Record<string, { entry: string }>"}
Register additional server environments as fetchable services.
::

## Testing with Vitest

[Vitest](https://vitest.dev) picks up the `nitro()` plugin from your Vite config and runs your tests against the Nitro runtime. See the [testing guide](/docs/testing) for setup and details.

## Builders

Nitro can bundle your server with one of three builders:

| Builder | Description |
| --- | --- |
| `rolldown` | Default. Rust-based bundler. Requires `rolldown` to be installed (installed on demand when building). |
| `rollup` | Battle-tested bundler with a mature plugin ecosystem. Requires `rollup` to be installed. |
| `vite` | Full-stack builds via the Vite plugin. Requires `vite` to be installed. |

### How the builder is selected

The builder is resolved in this order:

1. The `builder` option in your Nitro config.
2. The `NITRO_BUILDER` environment variable.
3. Auto-detection: if `vite` is installed and your `vite.config` uses the `nitro()` plugin, the `vite` builder is used.
4. Otherwise, Nitro defaults to `rolldown`. If `rolldown` is not installed (and no `rollupConfig` or `rolldownConfig` is set), `nitro dev` runs the server sources without a builder ([`builder: false`](/config#builder)) and `nitro build` offers to install it.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  builder: "rollup",
});
```

Or without touching the config file:

```bash
NITRO_BUILDER=rollup nitro build
```

If you explicitly select `rollup` or `vite` and the package is not installed, Nitro prompts to install it as a dev dependency. `rolldown` is installed the same way when building.

The `vite` builder prerenders routes with `rolldown` (resolved from your project or your `vite`). Without it (e.g. with Vite 7), the prerenderer runs the server sources without a builder.

### Which builder to pick

- Stick with **`rolldown`** for standalone servers and APIs. It is the fastest option.
- Pick **`rollup`** if you depend on Rollup-specific plugins or need maximum ecosystem compatibility.
- Use **`vite`** (usually via auto-detection) whenever you have a frontend built with Vite. It is the only builder that bundles client and server together.

### Customizing the bundler

To add build plugins for every builder (including `vite`, and `builder: false`), use [`buildPlugins`](/config#buildplugins). With `vite`, they apply to the server bundle (`nitro` environment) as bundler plugins, after Vite's own plugins; use Vite's `plugins` for Vite plugins.

```ts [nitro.config.ts]
import { defineConfig } from "nitro";
import myPlugin from "./my-plugin";

export default defineConfig({
  buildPlugins: [myPlugin()],
});
```

For advanced cases, you can pass extra configuration directly to the underlying bundler with `rollupConfig` or `rolldownConfig`:

```ts [nitro.config.ts]
import { defineConfig } from "nitro";

export default defineConfig({
  rolldownConfig: {
    // Extra Rolldown options and plugins
  },
  rollupConfig: {
    // Extra Rollup options and plugins
  },
});
```

Each option applies to its matching builder. With the `vite` builder, both are merged into the server bundle configuration depending on whether Vite runs on Rolldown (`rolldown-vite`) or Rollup.

::warning
These options are escape hatches. Prefer built-in Nitro options when available, as low-level bundler overrides can break across builders.
::

## Good to know

- **Server reloads instead of HMR**: changes to server-only modules trigger a server reload (and a browser refresh) rather than hot module replacement. Modules shared between client and server keep normal Vite HMR.
- **Dev server port**: the port is resolved from the `PORT` environment variable, then Vite's `server.port`, then Nitro's `devServer.port` (default: `3000`).
- **Environment files**: in both dev and build, Nitro loads `.env`, `.env.local`, and mode-specific variants (`.env.[mode]`, `.env.[mode].local`) following [Vite conventions](https://vite.dev/guide/env-and-mode#env-files). Existing environment variables take precedence over `.env` values.
- **Vite environments**: Nitro uses the [Vite environment API](https://vite.dev/guide/api-environment) and registers `client` and `nitro` environments (plus `ssr` when an SSR entry exists). You can configure the client entry point via `environments.client.build.rollupOptions.input` in your Vite config, as shown in the [SSR examples](/examples/vite-ssr-react).
