Vite Integration
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:
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.
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:
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
- SSR with Vue Router
- SSR with Solid
- SSR with Preact
- React Server Components
- SSR with plain HTML
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",
});
import { defineConfig } from "vite";
import { nitro } from "nitro/vite";
export default defineConfig({
plugins: [nitro()],
nitro: {
serverDir: "./server",
},
});
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) on the plugin object — see the Vite Nitro plugin example.
Experimental plugin options
The plugin additionally accepts Vite-specific flags under experimental.vite:
?assets imports for collecting CSS and JS assets from entry points (default: true).true).Builders
Nitro can bundle your server with one of three builders:
| Builder | Description |
|---|---|
rolldown | Default. Rust-based bundler, ships with Nitro — no extra installation needed. |
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:
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.
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
rolldownfor standalone servers and APIs — it is the fastest option and works out of the box. - Pick
rollupif 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:
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.
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
PORTenvironment variable, then Vite'sserver.port, then Nitro'sdevServer.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
clientandnitroenvironments (plusssrwhen an SSR entry exists). You can configure the client entry point viaenvironments.client.build.rollupOptions.inputin your Vite config, as shown in the SSR examples.