Concepts
Zone#
A zone is a normal Next.js app (the App Router, the Pages Router, or both), so it builds with
next build and runs alone with next start. It
declares itself in its next.config with zoneConfig. A zone never needs next-zones to run:
- On its own, it is just a Next app.
- Together with other zones, it is served by Zones, or built with them into one Next app (the composer).
The shell#
The shell is the zone mounted at /. There is no special flag: it is simply the owner of /. It holds what every
page shares:
- the root layout;
- sign-in and the proxy (
proxy.ts); - global instrumentation;
- every route that is not another zone's.
A set of zones has exactly one shell.
Mount#
Every zone other than the shell owns one URL segment, its mount, such as /blog. All of its routes live under it:
/blog, /blog/[id], /blog/settings… A mount has one owner. Two zones on the same mount are refused, and so is a
zone on a segment the shell already serves.
Alias#
A zone can also serve a URL at the root, outside its mount, such as /post/42 for its page /blog/42. That is an
alias: the page still lives under the mount, and the address bar shows the root URL.
Version#
A version of a zone is a build, not a copy of its source. Zone images are kept in a store (one folder per zone and version). Zones can switch between them, forwards or back, while the server runs.
The contract between zones#
Zones served together must share:
- one
node_modules: the same Next, the same React, the same layout (a monorepo workspace does this); - the same root layout output: in practice, every zone renders the same root layout component from a shared package;
- the same
basePath,i18n,trailingSlash,assetPrefix,skipTrailingSlashRedirect,cacheComponents,partialPrefetching(Zones refuses a zone that differs, and names the key); and noimageskey of a zone's own that differs from the shell's (a zone with noimagesconfig fits any shell).
Shared packages may differ in version between zones.
- Modules that are the same in two zones are loaded once and shared.
- A module that differs (another version of a shared component, or anything that imports it) runs in its own version in each zone, side by side in the same page.
- Mind the remount. A shared component that differs between two zones is two different components to React, so it mounts again when the user moves between them. Keep the root layout and providers on the same version in every zone to keep their state across zones.
zoneConfig sets the build options next-zones needs (for example, Turbopack scope hoisting off, so a module shared
by zones loads once).