
How to deploy Nuxt to Vercel, Netlify, Cloudflare, and Node, with hybrid rendering via routeRules.
Reza Baar
June 3, 2026
Nuxt deploys almost anywhere thanks to Nitro, the server engine underneath. Nitro has over 15 presets that adapt your build output for different hosting targets. You can also mix rendering strategies in a single app: some routes static, some SSR, some client-only. In this post, we'll cover the most common deployment targets, how hybrid rendering works, and the environment variable patterns you need to get right.
When you run nuxt build, Nitro compiles your server code into a format that matches the target platform. The preset controls what the build output looks like:
In most cases, Nitro auto-detects the preset based on your deployment environment. If you're deploying to Vercel, it detects that and uses the Vercel preset automatically. You can also set it explicitly:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'cloudflare-pages',
},
});
Vercel auto-detects Nuxt projects. Push your code to a Git repo, connect it in the Vercel dashboard, and it builds and deploys with the right preset.
If you need to configure anything:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'vercel',
},
});
For Vercel Edge Functions (runs at the edge instead of a single region):
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'vercel-edge',
},
});
Environment variables are set in the Vercel dashboard under Settings > Environment Variables. They're available in your Nuxt app through useRuntimeConfig():
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
secretApiKey: '', // NUXT_SECRET_API_KEY
public: {
apiBase: '', // NUXT_PUBLIC_API_BASE
},
},
});
Nuxt automatically maps environment variables with the NUXT_ prefix to the corresponding runtimeConfig keys.
Similar to Vercel, Netlify auto-detects Nuxt. Connect your repo and it picks up the framework settings. Manually:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'netlify',
},
});
For Netlify Edge Functions:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'netlify-edge',
},
});
Set the build command to nuxt build and the publish directory to dist in your Netlify settings (or netlify.toml):
# netlify.toml
[build]
command = "nuxt build"
publish = "dist"
Cloudflare Pages runs your server code on the Workers runtime at the edge. This gives you low latency globally but comes with some constraints (no Node.js fs access, limited Node APIs).
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'cloudflare-pages',
},
});
Deploy with Wrangler:
npx nuxt build
npx wrangler pages deploy dist/
Or connect your Git repo in the Cloudflare dashboard. Make sure to disable Cloudflare's "Rocket Loader" and "Email Address Obfuscation" under Speed and Security settings, as these inject scripts that can cause hydration errors.
For a traditional server deployment (Docker, a VPS, or any environment with Node.js):
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
preset: 'node-server',
},
});
Build and run:
nuxt build
node .output/server/index.mjs
The server starts on port 3000 by default. Set PORT and HOST environment variables to change this. Make sure NODE_ENV=production is set, because Vue Router strips development warnings only when this is present.
For Docker:
FROM node:20-slim
WORKDIR /app
COPY .output .output
ENV NODE_ENV=production
ENV HOST=0.0.0.0
ENV PORT=3000
EXPOSE 3000
CMD ["node", ".output/server/index.mjs"]
The .output directory contains everything needed to run the app. You don't need node_modules or source files in the container.
If your entire site can be pre-rendered at build time:
npx nuxi generate
This crawls all routes and generates static HTML files. The output goes to .output/public and can be deployed to any static hosting (GitHub Pages, S3, Cloudflare Pages in static mode).
For sites with many dynamic routes, tell Nuxt which routes to pre-render:
// nuxt.config.ts
export default defineNuxtConfig({
nitro: {
prerender: {
routes: ['/blog/post-1', '/blog/post-2'],
crawlLinks: true, // also pre-render pages linked from known routes
},
},
});
This is where Nuxt deployment gets interesting. You can assign different rendering strategies to different routes in a single app:
// nuxt.config.ts
export default defineNuxtConfig({
routeRules: {
// Pre-rendered at build time (static)
'/': { prerender: true },
'/about': { prerender: true },
// Incremental Static Regeneration: cached, revalidated every hour
'/blog/**': { isr: 3600 },
// Server-side rendered on every request (default)
'/dashboard/**': { ssr: true },
// Client-only SPA mode (no server rendering)
'/admin/**': { ssr: false },
// Simple redirect
'/old-page': { redirect: '/new-page' },
// Cache headers for API routes
'/api/**': {
cors: true,
headers: { 'cache-control': 'max-age=60' },
},
},
});
This means your landing page and about page are static (fast, cached at CDN), your blog uses ISR (fresh content without rebuilding the entire site), your dashboard is fully SSR (always current), and your admin panel is client-only (no server rendering overhead).
The ** glob matches all paths under that prefix.
ISR is especially useful for content-heavy sites. The first request triggers a server render and caches the result. Subsequent requests get the cached version. After the isr interval (in seconds), the next request triggers a background revalidation.
routeRules: {
'/blog/**': { isr: 3600 }, // cache for 1 hour
'/products/**': { isr: 300 }, // cache for 5 minutes
}
This works on platforms that support it (Vercel, Netlify, Cloudflare). On a Node.js server, Nitro handles the caching internally.
The pattern is the same everywhere. Define the shape in runtimeConfig, set values through environment variables with the NUXT_ prefix:
// nuxt.config.ts
export default defineNuxtConfig({
runtimeConfig: {
databaseUrl: '', // NUXT_DATABASE_URL (server only)
stripeSecret: '', // NUXT_STRIPE_SECRET (server only)
public: {
apiBase: '', // NUXT_PUBLIC_API_BASE (client + server)
siteUrl: '', // NUXT_PUBLIC_SITE_URL (client + server)
},
},
});
Access them in code:
// In a server route
const config = useRuntimeConfig();
const db = connectToDatabase(config.databaseUrl);
// In a component (only public config)
const config = useRuntimeConfig();
const apiUrl = `${config.public.apiBase}/posts`;
Where you set the actual values depends on the platform: Vercel/Netlify dashboards, Cloudflare's wrangler.toml, Docker .env files, or CI/CD pipeline variables. The runtimeConfig shape stays the same across all platforms.
After deploying, verify everything is working:
# Check which Nuxt/Nitro version and preset is active
npx nuxi info
# Preview the production build locally
npx nuxi preview
nuxi preview builds and serves the production output locally, using the same preset and rendering rules as the real deployment. This catches issues before they hit production.
.output directory is all you need.routeRules lets you mix static, ISR, SSR, and SPA rendering in one appNUXT_ prefix and map to runtimeConfig on all platformsnuxi preview to test production behavior locallyWe covered the most common deployment targets for Nuxt and how to configure hybrid rendering with routeRules. The combination of Nitro presets and per-route rendering strategies means you can optimize each part of your app independently without splitting it into separate projects.
Get the latest news and updates on developer certifications. Content is updated regularly, so please make sure to bookmark this page or sign up to get the latest content directly in your inbox.

State Management in React: useReducer, Context, and External Stores
Learn when to move beyond useState in React. This guide covers useReducer, the split-context pattern, external stores like Zustand, server state with TanStack Query, and useSyncExternalStore.
Aurora Scharff
Jun 4, 2026

Deploying Nuxt: Presets, Platforms, and Hybrid Rendering
How to deploy Nuxt to Vercel, Netlify, Cloudflare, and Node, with hybrid rendering via routeRules.
Reza Baar
Jun 3, 2026

Trigger options for @defer
Learn how Angular's @defer block triggers lazy loading and explore the different loading strategies available to control when components are rendered for better performance and user experience.
Alain Chautard
Jun 2, 2026