As Next.js applications scale to enterprise levels, monolithic architectures become bottlenecks. More teams, complex deployment pipelines, and growing codebases slow development velocity.
Next.js Multi-Zones provide an elegant solution: multiple independent Next.js applications serving different URL paths under a single domain. This micro-frontend approach maintains SEO and performance while enabling team autonomy.
In this comprehensive guide, we'll explore Multi-Zones from concept to production implementation, including the latest Next.js 15 features.
A Zone is a standalone Next.js application that serves a specific set of URL paths under a shared domain. Multi-Zones enable micro-frontend architecture by allowing different parts of your application to be developed, built, and deployed independently.
example.com/ → Main marketing app (Next.js 15)
example.com/dashboard/* → Dashboard app (Next.js 14)
example.com/blog/* → Blog app (Next.js 15)
example.com/admin/* → Admin panel (different framework)Each zone operates as a completely independent application with its own:
Large teams often struggle inside a single repository.
With Multi-Zones:
This setup works exceptionally well for enterprise-scale products.
Different parts of a product often serve different purposes:
Multi-Zones allow each area to evolve independently, with architecture that mirrors real business domains.
Migrating a legacy system to Next.js?
Multi-Zones let you:
No risky “big rewrite” required.
Multi-Zones operate through external routing rather than internal Next.js routing. The magic happens at the infrastructure layer:
Reverse Proxy (Recommended):
Next.js Rewrites (Alternative):
You can use one Next.js application as the router using next.config.js rewrites:
// next.config.js - Main app routing
module.exports = {
async rewrites() {
return [
{
source: '/blog/:path*',
destination: 'https://blog-app.vercel.app/blog/:path*'
},
{
source: '/dashboard/:path*',
destination: 'https://dashboard-app.vercel.app/dashboard/:path*'
}
]
}
}Each zone (except the default one) must configure an assetPrefix to avoid resource conflicts:
// next.config.js - Blog zone
module.exports = {
assetPrefix: '/blog',
// Next.js 15+ automatically handles static asset routing
}Next.js 15 Update: In versions prior to 15, additional rewrites were needed for static assets. Next.js 15 automatically handles this, making setup significantly simpler.
Let's walk through a complete two-zone setup with code examples:
Handles: /* (catch-all for unmatched routes)
// main-app/next.config.js
module.exports = {
// No assetPrefix needed for default zone
async rewrites() {
return [
{
source: '/blog/:path*',
destination: 'https://blog-app.vercel.app/blog/:path*'
}
]
}
}Handles: /blog/*
// blog-app/next.config.js
module.exports = {
assetPrefix: '/blog',
// Next.js 15+ automatically serves static assets at /blog/_next/
}// blog-app/src/app/layout.js
export default function RootLayout({ children }) {
return (
<html lang="en">
<head>
<base href="/blog" /> {/* Critical for relative paths */}
</head>
<body>{children}</body>
</html>
)
}Vercel (Simplest):
{
"rewrites": [
{ "source": "/blog/(.*)", "destination": "https://blog-app.vercel.app/blog/$1" }
]
}NGINX:
location /blog/ {
proxy_pass https://blog-app.vercel.app/blog/;
proxy_set_header Host $host;
}Understanding navigation behavior is crucial for Multi-Zones success:
Soft Navigation (within same zone):
<Link> componentHard Navigation (between zones):
<a> tags// Correct: Standard link for cross-zone navigation
<a href="/blog" className="text-blue-600 hover:underline">
Visit Our Blog
</a>
// Avoid: This will try to prefetch and soft navigate, causing issues
<Link href="/blog">
Visit Our Blog
</Link>
// Correct: Smart navigation component
function SmartLink({ href, children, ...props }) {
const isSameZone = href.startsWith('/dashboard'); // Your zone detection logic
return isSameZone ? (
<Link href={href} {...props}>
{children}
</Link>
) : (
<a href={href} {...props}>
{children}
</a>
);
}While zones are independent, strategic sharing maintains consistency and reduces duplication:
// Shared package: @company/design-system
// Each zone installs as dependency
// Usage in any zone:
import { Button, Card } from '@company/design-system';
import { CompanyLogo } from '@company/brand-assets';// Shared auth library
import { auth } from '@company/auth-library';
// All zones use same top-level domain:
// .example.com cookies are accessible across all zones
// Secure cross-zone auth flow
function login(returnUrl = '/') {
// Redirect to central auth zone
window.location.href = `/auth/login?return=${encodeURIComponent(returnUrl)}`;
}// Shared API client with automatic zone awareness
import { api } from '@company/api-client';
// Zone-aware API calls
const userData = await api.get('/api/user');
const products = await api.get('/api/products');
// The API client handles:
// - Authentication tokens
// - Zone-specific API endpoints
// - Error handling and retries// Shared feature flag service
import { features } from '@company/feature-flags';
// Consistent feature rollout across zones
if (features.isEnabled('newCheckout')) {
// Show new checkout experience
}Multi-Zones introduce operational complexity.
Avoid them if:
In these cases, a single Next.js app or a monorepo is usually the better choice.
Multi-Zones are a scaling strategy, not a default architecture.
They are most effective when:
If your Next.js application is becoming hard to scale organizationally, Multi-Zones might be the architectural unlock you need.
More articles you might find interesting
Have you ever tried to log or send state immediately after calling its set function, only to find the old value instead? This is one of the most common "aha!" moments for React developers.
Learn how to organize your next-intl translations into feature-based JSON files using namespaces, with parallel loading and fallback locale support for better maintainability in large Next.js applications.
Discover why using array indices as keys in React can lead to performance issues and bugs, and learn the best practices for proper list rendering.