A Nuxt.js app deployed on Vercel can be either server-side rendered or statically generated, and Vercel handles the routing between these two modes automatically based on your build output.
Let’s see it in action. Imagine you have a Nuxt.js app.
# nuxt.config.ts
export default defineNuxtConfig({
ssr: true, // Or false, Vercel adapts
target: 'server' // Or 'static'
})
When you push this to Vercel, the build process determines the output. If ssr is true and target is 'server' (or omitted, as 'server' is the default), Vercel spins up a Node.js serverless function for your SSR needs.
// vercel.json (auto-generated or manually configured)
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/nuxt",
"config": {
"serverless": true // Indicates SSR
}
}
]
}
If ssr is false or target is 'static', Vercel generates static HTML, CSS, and JavaScript files. These are then served directly from Vercel’s global CDN.
// vercel.json (auto-generated or manually configured)
{
"version": 2,
"builds": [
{
"src": "package.json",
"use": "@vercel/nuxt",
"config": {
"serverless": false // Indicates static generation
}
}
]
}
The magic happens when you have a hybrid setup. Suppose you have some pages that must be server-rendered (e.g., dynamic user dashboards) and others that can be statically generated (e.g., marketing pages). Nuxt 3’s target option combined with Vercel’s @vercel/nuxt builder intelligently handles this.
For a hybrid approach, you’d typically set ssr: true and target: 'server' in your nuxt.config.ts. Vercel, by default, will try to server-render everything. However, if you explicitly want to pre-render specific routes, you can use nuxt generate with specific routes or let Nuxt’s nitro engine decide at build time. Vercel’s builder is smart enough to detect pre-rendered routes and serve them as static assets, falling back to the serverless function for anything that remains dynamic.
Consider this nuxt.config.ts:
export default defineNuxtConfig({
ssr: true,
// Vercel's builder will automatically detect and pre-render routes
// if they can be generated statically.
})
When you run vercel --prod, Vercel analyzes your Nuxt app. It identifies routes that can be pre-rendered (e.g., pages without dynamic data fetching that relies on the request context) and generates static files for them. Routes that require server-side logic (like fetching user-specific data) will be handled by a serverless function. This means your static assets are lightning-fast on the CDN, while dynamic content is still generated on demand.
The underlying mechanism is Vercel’s integration with Nitro, Nuxt’s server engine. Nitro builds your application into an output directory. For SSR, it produces a server handler. For static generation, it produces static files. Vercel’s build process inspects this output. If it finds static files (.html, .css, .js) in the appropriate dist or .output/public directory, it configures its CDN to serve those directly. Any routes not found as static files are then routed to the serverless function generated from the server handler.
The most surprising true thing is that Vercel’s @vercel/nuxt builder abstracts away the complexity of deciding which routes become static and which remain dynamic. By default, if your Nuxt app is configured for SSR, Vercel will attempt to serve everything via its serverless function. However, if Nitro, during the build process, generates static files for certain routes (which it does for routes that don’t explicitly require server-side rendering on every request), Vercel’s infrastructure automatically identifies these and serves them from the edge cache, effectively creating a hybrid deployment without explicit configuration for each route.
This means you can often achieve optimal performance by simply setting ssr: true and letting Vercel’s build process and Nitro’s output detection do the heavy lifting, ensuring static routes are cached globally and dynamic routes are handled efficiently by serverless functions.
The next step is understanding how to leverage Vercel’s edge functions for even more granular control over request routing and response manipulation at the edge.