Pre-rendering
When using NuxtHub, you need to build your application with the nuxt build
in order to keep your server when deploying your application.
However, you can also pre-render your pages at build time to improve performance and avoid CPU usage on the server.
Route Rules
Globally
You can define route rules in your nuxt.config.ts
to specify how each route should be rendered:
export default defineNuxtConfig({
routeRules: {
'/': { prerender: true }
}
})
When running nuxt build
, Nuxt will pre-render the /
route and save the index.html
file in the output directory.
Page Level
It is also possible to define route rules at the page level using the defineRouteRules
util in the page component:
<script setup lang="ts">
defineRouteRules({
prerender: true
})
</script>
<template>
<h1>Hello world!</h1>
</template>
This is equivalent of our example above in the nuxt.config.ts
file.
Notes:
- A rule defined in
~/pages/foo/bar.vue
will be applied to/foo/bar
requests. - A rule in
~/pages/foo/[id].vue
will be applied to/foo/**
requests.
nuxt.config.ts
files with experimental: { inlineRouteRules: true }
.Dynamic Pre-rendering
In some cases, you may want to Nuxt to pre-render other pages when pre-rendering a specific page.
This is useful when pre-rendering all the blog posts when pre-rendering the blog index page. To achieve this, we can use the prerenderRoutes
util in the page component:
<script setup lang="ts">
// Pre-render the /blog page
defineRouteRules({ prerender: true })
const { data: posts } = await useFetch('/api/posts')
// Tell Nuxt to pre-render all blog posts
prerenderRoutes(posts.value.map(post => `/blog/${post.slug}`))
</script>
It is important to tell Nuxt to pre-render the /blog
using the defineRouteRules
, we can also do it globally in the nuxt.config.ts
file.
export default defineNuxtConfig({
routeRules: {
// If not using `defineRouteRules` in the page component
'/blog': { prerender: true }
}
})
Using a Nuxt Module
You can also use a local Nuxt module to pre-render dynamic pages, which is particularly useful if you don't have a single root page (such as /blog
) but still need to pre-render specific routes, such as /page-1
, /parent/page-2
, and so on.
import { defineNuxtModule, addPrerenderRoutes } from '@nuxt/kit'
export default defineNuxtModule({
meta: {
name: 'nuxt-prerender-routes',
},
async setup() {
const pages = await getDynamicPages()
addPrerenderRoutes(pages)
},
})
async function getDynamicPages(): string[] {
// Replace this function with the logic for retrieving the slugs for your pages.
return ['/page-1', '/parent/page-2']
}
Pre-render All Pages
To have the same behavior as nuxt generate
while keeping the server part, you can pre-render all pages by configuring the nitro.prerender
option in the nuxt.config.ts
:
export default defineNuxtConfig({
nitro: {
prerender: {
// Pre-render the homepage
routes: ['/'],
// Then crawl all the links on the page
crawlLinks: true
}
}
})
When running nuxt build
, Nuxt will pre-render all pages and save the index.html
file in the dist/
directory.
Cloudflare 100 routes limit
NuxtHub will generate a dist/_routes.json
for Cloudflare Pages, but it has a limit of 100 excluded routes (used for static assets).
As each pre-rendered page will be added to the exclude list, we recommend to add your known pre-rendered pattern in the nitro.cloudflare.pages.routes.exclude
option:
export default defineNuxtConfig({
// ...
nitro: {
cloudflare: {
pages: {
routes: {
exclude: [
// we know that all docs and blog pages are pre-rendered
'/docs/*',
'/blog/*'
]
}
}
}
}
})
Caveats
If you are using authentication in your application such as nuxt-auth-utils
, you need to remember that the authentication state will not be available during the pre-rendering process.
For example, if you have a header component that either display the logged in user or a login button, you need to wrap the logic inside the <AuthState>
component to display a placeholder while the page is being pre-rendered.
<script setup lang="ts">
const { loggedIn, user } = useUserSession()
const links = [
{ label: 'Docs', to: '/docs' },
{ label: 'Blog', to: '/blog' }
]
</script>
<template>
<UHeader title="ACME" :links="links">
<template #right>
<AuthState>
<UButton v-if="loggedIn" to="/profile">{{ user.name }}</UButton>
<UButton v-else to="/login">Login</UButton>
<template #placeholder>
<!-- Display loading state -->
<UButton :loading="true" />
</template>
</AuthState>
</template>
</template>