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

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.
  • vite preview — Serves the production build locally through Nitro, including static assets and WebSocket support.
Follow the quick start for a step-by-step guide to adding Nitro to an existing Vite project.
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:

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:

Learn more about how unmatched routes are rendered in the renderer and server entry guides.

Configuring Nitro with Vite

Nitro configuration can live in three places, all accepting the same options:

A nitro.config.ts file (recommended for most projects)

The nitro key in your Vite config

Inline plugin options passed to nitro()

import { defineConfig } from "nitro";

export default defineConfig({
  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) on the plugin object — see the Vite Nitro plugin example.

Experimental plugin options

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

assetsImport
boolean
Enable ?assets imports for collecting CSS and JS assets from entry points (default: true).
serverReload
boolean
Reload the server (and browser) when a server-only module changes (default: true).
services
Record<string, { entry: string }>
Register additional server environments as fetchable services.

Builders

Nitro can bundle your server with one of three builders:

BuilderDescription
rolldownDefault. Rust-based bundler, ships with Nitro — no extra installation needed.
rollupBattle-tested bundler with a mature plugin ecosystem. Requires rollup to be installed.
viteFull-stack builds via the Vite plugin. Requires vite to be installed.

How the builder is selected

The builder is resolved in this order:

The builder option in your Nitro config.

The NITRO_BUILDER environment variable.

Auto-detection: if vite is installed and your vite.config uses the nitro() plugin, the vite builder is used.

Otherwise, Nitro defaults to rolldown.

nitro.config.ts
import { defineConfig } from "nitro";

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

Or without touching the config file:

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.

Which builder to pick

  • Stick with rolldown for standalone servers and APIs — it is the fastest option and works out of the box.
  • 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

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

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.

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 dev mode, Nitro loads .env, .env.local, and mode-specific variants (.env.[mode], .env.[mode].local) following Vite conventions.
  • Vite environments: Nitro uses the Vite environment API 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.