NuxtBuilder interface definition
A builder must implement the NuxtBuilder interface. The only required method is `bundle(nuxt: Nuxt): Promise<void>`. An optional method is `setupWatcher?(nuxt: Nuxt): Promise<void> | void`, which Nuxt calls instead of starting its own file watcher when the user opts in via `experimental.watcher: 'builder'`. The builder should register a `nuxt.hook('close', ...)` to clean up when setupWatcher is implemented.
Builder configuration in nuxt.config.ts
The builder is specified via the `builder` option in nuxt.config.ts. It accepts either a module specifier string that default-exports a NuxtBuilder (such as '@nuxt/vite-builder'), or an inline object implementing the NuxtBuilder interface.
Official builders shipped with Nuxt
Nuxt ships with three official builders: Vite (the default), webpack, and Rspack. These can be selected via the `builder` option in nuxt.config.ts.
Builder responsibilities
A builder is responsible for: bundling the client build (browser bundle) and, when SSR is enabled, the server build (SSR app entry); producing artifacts the server runtime needs to render and hydrate (client manifest, per-component styles map, etc.); and in development, exposing a dev server and triggering reloads when the build changes. The deployable server itself is produced by Nitro via @nuxt/nitro-server, not by the builder.
When builder.bundle is called in Nuxt lifecycle
Nuxt calls `bundle(nuxt)` once during `nuxt build` and `nuxt dev`, after the virtual application has been generated and the `build:before` hook has fired. This occurs in step 4 of the Nuxt build lifecycle. Nuxt wraps the bundle call so that any thrown error automatically triggers the `build:error` hook.
Complete Nuxt build lifecycle
When running `nuxt build` or `nuxt dev`, Nuxt performs these steps in order: 1) Creates the nuxt context and runs modules, populating nuxt.options and build hooks. 2) Generates the virtual application (templates, route table, plugins) into the #build virtual file system. 3) Fires the build:before hook. 4) Resolves the builder and calls builder.bundle(nuxt). 5) Fires the build:done hook, and in production closes the nuxt instance.
Builder branching on nuxt.options.dev
A builder's bundle implementation should branch on nuxt.options.dev. In production, it runs client and server builds to completion, writing artifacts to nuxt.options.buildDir and registering them as build outputs. In development, it sets up a dev server, starts a watching build, assigns nuxt.server, and keeps running.
Module helpers for bundler configuration
Modules influence the bundle through Nuxt Kit helpers that builders should honour: addVitePlugin/addWebpackPlugin register bundler-specific plugins; addBuildPlugin registers an unplugin factory that works across every builder; extendViteConfig/extendWebpackConfig mutate the resolved bundler config.
NuxtBuildOutputs interface keys and types
The NuxtBuildOutputs interface defines the build output contract with these keys: serverEntry (function returning string/Promise<string>): module body re-exporting the SSR app entry; ssrStyles (string or undefined): path to emitted per-component SSR styles map; clientManifest (function returning string/Promise<string>): serialized client manifest for vue-bundle-renderer; clientPrecomputed (function returning string/Promise<string>): serialized precomputed client dependency data; entryChunkName (function returning string/Promise<string>): module body exporting hashed entry chunk filename for import maps; entryIds (function returning string/Promise<string>): module body exporting entry module IDs for inline style extraction.
Build output to nuxt subpath mapping
Build outputs map to nuxt/* subpaths that the server runtime imports: serverEntry → nuxt/entry (SSR app factory); clientManifest → nuxt/manifest (vue-bundle-renderer manifest); clientPrecomputed → nuxt/precomputed (precomputed dependency data); ssrStyles → nuxt/styles (per-component inline-styles map); entryChunkName → nuxt/entry-chunk (hashed entry chunk filename); entryIds → nuxt/entry-ids (entry module IDs for style extraction).
Value providers vs emitted-file paths in build outputs
Build outputs come in two shapes: Value providers (serverEntry, clientManifest, clientPrecomputed, entryChunkName, entryIds) are functions returning module body as a string, which is inlined verbatim into the server bundle and must not depend on file location on disk. Emitted-file path (ssrStyles) is an absolute path to a real module the builder emitted, allowing the runtime to resolve the file's location and relative sibling imports. Leave ssrStyles as undefined when the build produces no inline styles.
setBuildOutput helper usage
Use the setBuildOutput helper from @nuxt/kit to set build outputs. It writes to nuxt.buildOutputs[key]. From inside a bundler plugin with access to the nuxt instance, you can assign nuxt.buildOutputs[key] directly. setBuildOutput is a convenience for code that resolves nuxt via useNuxt(). A provider can be asynchronous and is read lazily when the server build resolves the corresponding nuxt/* import.
Minimal builder example for production
A minimal builder skeleton for a production build:
```ts
import { resolve } from 'node:path'
import { pathToFileURL } from 'node:url'
import { setBuildOutput } from '@nuxt/kit'
import type { NuxtBuilder } from '@nuxt/schema'
export const bundle: NuxtBuilder['bundle'] = async (nuxt) => {
const serverDir = resolve(nuxt.options.buildDir, 'dist/server')
// ...run your client and server bundles here, writing artifacts to disk...
const { serializedClientManifest } = await runBundles(nuxt, serverDir)
if (nuxt.options.ssr) {
// Point `nuxt/entry` at the built SSR app entry.
const serverEntryURL = pathToFileURL(resolve(serverDir, 'server.mjs')).href
setBuildOutput('serverEntry', () => `export { default } from ${JSON.stringify(serverEntryURL)}`)
// Provide the client manifest produced by your client build.
setBuildOutput('clientManifest', () => `export default ${serializedClientManifest}`)
// If you emit a per-component styles map alongside its CSS chunks:
setBuildOutput('ssrStyles', resolve(serverDir, 'styles.mjs'))
}
}
```
This example shows how to set serverEntry, clientManifest, and ssrStyles build outputs for an SSR build.
Development server responsibilities
In development, a builder must populate nuxt.server with the running dev server. The Nuxt CLI consumes it; official builders expose a handler (Node request listener), a fetch (web fetch handler), and reload/close methods. The builder should call nuxt.server.reload() when compilation finishes to signal reloads. Official builders emit hooks like vite:compiled or webpack:compiled that server integration listens to for reloading.
Development build outputs wiring
In development, build outputs are typically wired to live, in-memory sources rather than on-disk files. For example, the SSR entry may be served from the bundler's in-memory output, and the client manifest may be computed from the dev module graph rather than read from nuxt.options.buildDir.
Builder-agnostic contract and Vite Environment API
The build output contract is deliberately builder-agnostic and works for both the legacy path (each builder runs its own bundle, Nitro bundles the deployable separately) and the Vite Environment API path (Nitro runs as a Vite environment). Custom builders only need to satisfy the generic contract. The Vite Environment API integration (experimental.nitroViteEnvironment) is specific to @nuxt/vite-builder; other builders including custom ones use the legacy Nitro Rollup path.
When to author a custom builder
Authoring a builder is an advanced topic. Most apps never need a custom builder. If you only want to influence the bundle, a module that registers bundler plugins is usually the right tool instead of creating a custom builder.
Official builder recognition in Nuxt
Only the three official builder specifiers (@nuxt/vite-builder, @nuxt/webpack-builder, @nuxt/rspack-builder) are recognised by name for builder-specific behaviour elsewhere in Nuxt. A custom builder still works through the generic contract but is treated as the legacy (non-Vite-environment) path.
Default build outputs
The defaults for build outputs are sensible, so you rarely need to provide every key. Default values are: an empty manifest for clientManifest, no inline styles for ssrStyles (leave as undefined), and an undefined entry chunk for entryChunkName. When SSR is disabled, the serverEntry default is a no-op app and most other outputs are unused.