Prerendering
When prerendering is enabled, Nitro fetches the specified routes during the build and writes the responses to the .output/public directory. Prerendered routes are then served as static files — no server rendering happens at request time, which makes them fast, cacheable, and deployable to any static hosting or CDN.
Prerendering is a good fit for routes whose content does not change per request: landing pages, documentation, blog posts, or JSON endpoints with constant output. Dynamic routes can keep being served by the server — you can freely mix prerendered and server-rendered routes in the same app.
Prerendering routes
prerender.routes config
List the routes to prerender using the prerender.routes option:
import { defineConfig } from "nitro";
export default defineConfig({
prerender: {
routes: ["/", "/about", "/api/content.json"],
},
});
HTML responses are written as index.html files within subfolders (/about produces about/index.html), while other responses keep their path (/api/content.json produces api/content.json). See autoSubfolderIndex to change this behavior.
prerender route rule
Alternatively, mark routes for prerendering with the prerender route rule:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
"/about": { prerender: true },
},
});
"/blog/**": { prerender: true } are not auto-expanded, since Nitro cannot know which routes match the pattern. To prerender a dynamic set of routes, use link crawling or the prerender:routes hook.Crawling links
Instead of (or in addition to) listing every route, enable crawlLinks to let Nitro discover routes by following links in prerendered HTML pages:
import { defineConfig } from "nitro";
export default defineConfig({
prerender: {
crawlLinks: true,
routes: ["/"],
},
});
The crawler extracts href attributes from each prerendered HTML page and queues the discovered routes for prerendering. External links, hash links, and links to files with extensions (except .json) are skipped. Relative links are resolved against the page they were found on.
If crawlLinks is enabled and no routes are specified, crawling starts from /.
x-nitro-prerender response header to a comma-separated list of paths. This works even when crawlLinks is disabled.Ignoring routes
Use prerender.ignore to skip routes. Each entry can be a string prefix, a regular expression, or a function receiving the path:
import { defineConfig } from "nitro";
export default defineConfig({
prerender: {
crawlLinks: true,
ignore: [
"/admin", // Ignore routes starting with /admin
/\/preview$/, // Ignore routes ending with /preview
(path) => path.includes("draft"), // Ignore routes containing "draft"
],
},
});
You can also exclude routes with the prerender: false route rule. Unlike enabling prerendering, wildcard patterns do work for exclusion since they are matched against each candidate route:
import { defineConfig } from "nitro";
export default defineConfig({
routeRules: {
"/dynamic/**": { prerender: false },
},
});
Public assets
Routes served by public assets with a base URL prefix are always skipped. Additionally, you can set prerender.ignoreUnprefixedPublicAssets: true to skip prerendering routes that are already served by unprefixed public assets, avoiding duplicate files in the output:
import { defineConfig } from "nitro";
export default defineConfig({
prerender: {
crawlLinks: true,
ignoreUnprefixedPublicAssets: true,
},
});
Options
| Option | Default | Description |
|---|---|---|
failOnError | false | Fail the build when a route cannot be prerendered. Useful in CI. |
retry | 3 | Number of times to retry a failed route. Pass Infinity to retry indefinitely. |
retryDelay | 500 | Delay between each retry in milliseconds. |
concurrency | 1 | Maximum number of routes prerendered in parallel. |
interval | 0 | Delay in milliseconds between prerender requests. |
autoSubfolderIndex | true | Write HTML routes as subfolder indexes. |
retry and retryDelay options are accepted by the config but are not applied by the current v3 prerenderer yet.The concurrency and interval options control prerendering speed. Increase concurrency to speed up large sites, or add an interval to avoid hitting rate limits when routes call external APIs.
autoSubfolderIndex controls how HTML files are written to .output/public:
# autoSubfolderIndex: true (default)
/about -> .output/public/about/index.html
# autoSubfolderIndex: false
/about -> .output/public/about.html
Set it to false when your hosting provider does not let you configure trailing slash behavior.
import { defineConfig } from "nitro";
export default defineConfig({
prerender: {
crawlLinks: true,
failOnError: true,
concurrency: 8,
autoSubfolderIndex: false,
},
});
Hooks
For advanced use cases, you can hook into the prerendering lifecycle at build time. These hooks are available to modules and the hooks config option:
| Hook | Description |
|---|---|
prerender:routes | Called with the initial Set of routes — add or remove entries before prerendering starts. |
prerender:config | Called with the config of the internal Nitro instance used for prerendering. |
prerender:generate | Called for each route before its file is written — inspect or modify the route object (route.contents, route.fileName, ...) or set route.skip = true. |
prerender:route | Called after each route is generated. |
prerender:done | Called once prerendering is finished, with the lists of prerendered and failed routes. |
A common use case is registering routes dynamically, for example from a CMS or database:
import { defineConfig } from "nitro";
export default defineConfig({
modules: [
(nitro) => {
nitro.hooks.hook("prerender:routes", async (routes) => {
const posts = await fetch("https://api.example.com/posts").then((res) => res.json());
for (const post of posts) {
routes.add(`/blog/${post.slug}`);
}
});
},
],
});
Static hosting
To ship a fully prerendered site without a server bundle, use a static preset such as static, github_pages, or gitlab_pages. These presets skip the server build, enable crawlLinks, and only output the public directory, ready to upload to any static hosting. See the deploy documentation and providers like GitHub Pages for details.
static: true config option only disables the server build — it does not enable prerendering by itself. When using it directly instead of a static preset, configure prerender options (typically crawlLinks: true) to generate the pages.