Build

Bundler selection and build output tuning.

Note

All examples on this page assume the following import in nitro.config.ts:

import { defineConfig } from "nitro";

#builder

  • Type: "rollup" | "rolldown" | "vite" | false
  • Default: undefined (auto-detected)

Specify the bundler to use for building.

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

When not set, Nitro uses vite if your vite.config uses the nitro() plugin, otherwise rolldown. The builder package is resolved from your project (rolldown also through your vite) and installed on demand when building. In development, if rolldown is not installed and no rollupConfig or rolldownConfig is set, nitro dev runs without a builder (builder: false) and nitro build offers to install it.

Set builder: false (experimental) to run the server sources without bundling. nitro dev then runs your files directly in an env-runner worker, Nitro's virtual modules are served from memory, and a fresh worker starts on each change. nitro start (or nitro serve) runs the sources the same way in production mode, without a build step, whatever builder is configured. nitro preview only serves a build output. Prerendering runs the sources the same way, and is also used by the vite builder when rolldown is not installed. nitro build is not supported yet.

Modules are transformed on demand, like in a bundled build: TypeScript and JSX, import.meta.* and replace values, alias, extensionless imports, raw:/text/bytes imports and WebAssembly.

Note

  • On Bun, aliases without a file extension (such as ~/utils) and WebAssembly imports are not resolved yet.
  • Files outside the project, its scan dirs and Nitro (such as linked workspace packages) are loaded as-is, without these transforms.
  • Bundler options (rollupConfig, rolldownConfig) and plugins added by modules with the build:config hook do not apply. Use buildPlugins for plugins.

#buildPlugins

  • Type: NitroBuildPluginOption[]
  • Default: []

Build plugins applied with every builder (rollup, rolldown, vite and false). Nested arrays and promises are resolved, falsy entries are skipped.

Plugins use the Rollup plugin interface. Prefer the resolveId, load and transform hooks with a filter: they are the ones supported by all builders.

Plugins with enforce: "pre" are placed before Nitro's own plugins, the others after them (enforce: "post" last). A hook's own order takes precedence: several Nitro hooks (such as virtual module resolution) use order: "pre", so use it too for a hook that must run before them.

Note

  • Builders differ in what runs around the plugins. transform hooks receive TypeScript and JSX untransformed with rolldown (it compiles them after the plugins) and for enforce: "pre" plugins with rollup. With vite, Vite's own resolution and transforms run first.
  • With vite, plugins apply to the nitro environment only, as bundler plugins: Vite-specific options and hooks (such as apply or configureServer) are not supported. Add Vite plugins to Vite's plugins instead.
  • With builder: false, plugins run in env-runner, which supports only resolveId, load and transform (with a limited plugin context) and warns about other hooks. Hooks only receive JavaScript and TypeScript modules unless their filter names other files (by id or moduleType), and files in node_modules only when it names node_modules.
export default defineConfig({
  buildPlugins: [
    {
      name: "my-virtual-module",
      resolveId: {
        filter: { id: /^virtual:my-module$/ },
        handler: (id) => `\0${id}`,
      },
      load: {
        filter: { id: /^\0virtual:my-module$/ },
        handler: () => `export default "Hello!"`,
      },
    },
  ],
});

Nitro modules can add plugins with nitro.options.buildPlugins.push(plugin).

#rollupConfig

Additional rollup configuration.

export default defineConfig({
  rollupConfig: {
    output: { manualChunks: { vendor: ["lodash-es"] } },
  },
});

#rolldownConfig

Additional rolldown configuration.

export default defineConfig({
  rolldownConfig: {
    output: { banner: "/* built with nitro */" },
  },
});

#vite

Options for the vite builder and the nitro/vite plugin.

  • path: The vite package to use, as a path or file:// URL to its directory or entry (same as deps.paths.vite). By default, the nitro/vite plugin uses the running vite and the vite builder resolves it from the project root.
  • services: Additional Vite environments to register as fetchable services, as { [name]: { entry } }. Server environments with an entry (such as ssr) are registered automatically.
  • assetsImport: Enable ?assets imports for collecting CSS and JS assets from entry points (default: true).
  • serverReload: Reload the dev server (and the browser) when a server-only module changes (default: true).
export default defineConfig({
  vite: {
    path: import.meta.resolve("vite"),
    services: { api: { entry: "./src/api-entry.ts" } },
  },
});

#deps

Options for the optional dependencies Nitro installs on demand (such as vite, rolldown, rollup, unstorage, db0 or unenv).

  • paths: Explicit packages to use, by package name. Each value is a path or file:// URL to the package directory or entry. Relative paths resolve against rootDir.

By default, these packages are resolved from your project and installed on demand when missing. A framework built on top of Nitro can pass its own copies in deps.paths instead, so the versions it depends on are the ones that run. The server bundle uses them too for unstorage, db0 and unenv. An explicit package is never installed, and Nitro warns when its version is outside the supported range. Nothing is installed either when rootDir is inside node_modules (e.g. a framework using its own package directory): the framework has to declare the packages it uses in its own dependencies or pass them in deps.paths.

export default defineConfig({
  deps: {
    paths: {
      unstorage: import.meta.resolve("unstorage"),
      db0: import.meta.resolve("db0"),
    },
  },
});

deps.paths.vite is an alias of vite.path.

#entry

Bundler entry point.

export default defineConfig({
  entry: "./server/entry.ts", // custom entry file
});

#alias

Path aliases for module resolution.

export default defineConfig({
  alias: {
    "~utils": "./src/utils",
    "#shared": "./shared",
  },
});

With the Vite builder, these aliases apply to every Vite environment. When Vite's resolve.alias (from the Vite config or another plugin, with a string find) maps the same key to another target, the Nitro target applies only to the nitro environment, and other environments (such as client and ssr) use the Vite target.

#inject

Globals to replace with imports when referenced in the bundle. Values are a module id (default export) or a [id, exportName] tuple.

Set a global to false to remove a default injection (e.g. one added for node: false builds).

export default defineConfig({
  inject: {
    Buffer: ["node:buffer", "Buffer"],
    performance: false,
  },
});

#polyfills

Modules imported for their side effects before the server entry. Prefix an entry with ! to remove a default polyfill.

node: false builds add #nitro/runtime/polyfills/global, #nitro/runtime/polyfills/process, #nitro/runtime/polyfills/buffer and #nitro/runtime/polyfills/timers by default (e.g. "!#nitro/runtime/polyfills/timers" removes the setImmediate polyfill).

export default defineConfig({
  polyfills: ["./polyfills/intl.ts"],
});

#builtinModules

Modules provided by the target runtime, such as cloudflare:workers or node:fs on Deno. Imports of these ids are kept as-is in the output.

Builtin modules are never bundled, traced, or copied to the output, so only list modules that the runtime itself provides. To keep an npm package out of the bundle, use traceDeps instead. Node.js builtins are handled automatically when node is true.

Prefix an entry with ! to remove a default (e.g. one added by a preset).

export default defineConfig({
  builtinModules: ["cloudflare:workers"],
});

Note

Presets also add default alias, inject, polyfills, and builtinModules entries (for example, Cloudflare and Deno map Node.js builtins they support natively). Your own values in these options always take precedence.

The unenv option is deprecated. Presets passed to it still apply, after preset defaults and before your own options.

#minify

  • Default: false

Minify bundle.

Some presets (especially worker runtimes) enable minification by default. Set minify: false when debugging production, worker, or edge builds so stack traces stay readable.

export default defineConfig({
  minify: true, // minify production bundle
});
export default defineConfig({
  minify: false, // easier to debug workers / edge bundles
});

#inlineDynamicImports

  • Default: false

Bundle all code into a single file instead of creating separate chunks per route.

When false, each route handler becomes a separate chunk loaded on-demand. When true, everything is bundled together. Some presets enable this by default.

export default defineConfig({
  inlineDynamicImports: true, // single output file
});

#sourcemap

  • Default: false

Enable source map generation. See options.

export default defineConfig({
  sourcemap: true, // generate .map files
});

#node

  • Default: true

Specify whether the build is used for Node.js or not. If set to false, nitro adds default alias, inject and polyfills entries that polyfill Node.js globals and built-in modules (using unenv), and adjusts its behavior.

Node.js built-in modules that the target runtime does not provide (e.g. node:inspector on Cloudflare) are polyfilled with unenv. unenv is resolved from your project and installed on demand, only when the build imports such a module.

export default defineConfig({
  node: false, // target non-Node.js runtimes
});

#replace

Build-time string replacements.

export default defineConfig({
  replace: {
    "process.env.APP_VERSION": JSON.stringify("1.0.0"),
  },
});

#commonJS

Specifies additional configuration for the rollup CommonJS plugin.

export default defineConfig({
  commonJS: {
    requireReturnsDefault: "auto",
  },
});

#exportConditions

Custom export conditions for module resolution.

export default defineConfig({
  exportConditions: ["worker", "production"],
});

#noExternals

  • Default: false

Prevent packages from being externalized. Externalized packages are imported at runtime instead of being bundled, so they do not see your alias configuration.

In a production build Nitro already bundles almost everything: only native or otherwise non-bundleable packages and the ones listed in traceDeps stay external. In development, dependencies inside node_modules are kept external for speed, which is where this option matters most.

Set it to true to bundle everything, including packages that are normally kept external:

export default defineConfig({
  noExternals: true, // bundle all dependencies
});

#Inlining specific packages

Pass an array to inline only some modules. RegExp entries are used as-is; string entries are escaped and matched as a path substring.

Each pattern is tested against the import specifier as written (for example @scope/my-lib) and against the resolved absolute file path (for example /…/node_modules/@scope/my-lib/dist/index.mjs). A match on either one keeps the module bundled, so a plain package name is usually all you need:

export default defineConfig({
  alias: {
    "@shared": "./shared",
  },
  noExternals: ["@scope/my-lib"],
});

Use a path pattern when you need to be more precise, for example to inline one package in a monorepo without matching a similarly named one:

export default defineConfig({
  noExternals: [/packages[\\/]my-lib/],
});

Write [\\/] instead of / in such patterns so they also match Windows paths.

Note

In Nitro 2 this option was externals.inline.

#traceDeps

  • Default: []

Additional dependencies to trace and include in the build output.

Supports special prefixes:

  • !pkg: Exclude a built-in package from tracing.
  • pkg*: Full trace, copies all package files instead of only traced ones.
export default defineConfig({
  traceDeps: [
    "sharp",
    "better-sqlite3",
    "my-pkg*", // full trace (copy all package files)
    "!unwanted-pkg", // exclude from tracing
  ],
});

#traceOpts

Advanced options passed to nf3 for dependency tracing.

export default defineConfig({
  traceOpts: {
    // Options passed to @vercel/nft for file tracing
    nft: { /* ... */ },
    // Alias for module paths when tracing
    traceAlias: { "old-pkg": "new-pkg" },
    // Preserve or set file permissions when copying (true or octal like 0o755)
    chmod: true,
    // Transform traced files before copying
    transform: [
      { filter: (id) => id.endsWith(".js"), handler: (code) => code },
    ],
    // Hook into tracing lifecycle
    hooks: {
      tracedPackages(pkgs) {
        console.log("Traced packages:", Object.keys(pkgs));
      },
    },
  },
});

#oxc

OXC options for the rollup builder. Includes minify and transform sub-options.

minify uses the minifier of rolldown. If rolldown is not installed, minification is skipped with a warning.

export default defineConfig({
  oxc: {
    minify: { compress: true, mangle: true },
  },
});

Nitro  builds full-stack servers that deploy anywhere.