# Getting started > Requires Next.js 16.3.6, 16.3.7, 16.3.8 or 16.4.0 and Node.js 24 or later. Zones use the App Router, the > [Pages Router](pages-router.md), or both; this guide uses the App Router. ## The quick way ```sh npx @runsnip/next-zones init my-app blog shop # a workspace: shared/, shell/, blog/, shop/, installed cd my-app npm run dev # every zone on one next dev, with HMR: http://localhost:3000 npm run doctor # checks the workspace for Zones and for dev npx next-zones add docs # one more zone, at /docs ``` `init` uses the package manager that runs it (npm, yarn, pnpm or bun) and never overwrites a file. Every tsconfig extends `@runsnip/next-zones/tsconfig/zone.json`, and each zone keeps its own `@/*` → `./src/*`. The steps below are what it sets up, for a workspace made by hand. ## 1. A workspace of zones One workspace (npm, yarn or pnpm), so every zone shares one `node_modules`: ``` my-app/ package.json workspaces: ["shared", "shell", "blog", "shop"] shared/ a package: the root layout, shared UI shell/ the zone mounted at "/" blog/ the zone mounted at "/blog" shop/ the zone mounted at "/shop" ``` ```json { "private": true, "workspaces": ["shared", "shell", "blog", "shop"], "dependencies": { "@runsnip/next-zones": "0.x" } } ``` ## 2. Declare each zone Each zone's `next.config.mjs` (or `.ts`): ```js // shell/next.config.mjs import { zoneConfig } from "@runsnip/next-zones/config"; export default zoneConfig({ mount: "/", endpoints: { events: true, health: true, admin: true }, // Zones' own URLs, under /_next-zones (see below) transpilePackages: ["shared"], }); ``` `endpoints` is the shell's only. `events` feeds [``](updates.md), `health` answers a supervisor, and `admin` is what `next-zones install`, `pull --url` and `prune --url` talk to: without it they get a 404. See [endpoints](zones.md#endpoints). ```js // blog/next.config.mjs import { zoneConfig } from "@runsnip/next-zones/config"; export default zoneConfig({ mount: "/blog", transpilePackages: ["shared"], }); ``` Every route of `blog` lives under `blog/app/blog/…`. At the top of `blog/app/` there may also be the root files Next needs when the zone runs alone (`layout`, `not-found`, `global-error`, `global-not-found`, `error`, `loading`, `template`, `default`, and CSS files); on Zones the shell's are used. ## 3. Share the root layout Every zone renders the same root layout, so a page looks the same however it is reached: ```tsx // shared/root-layout.tsx import Link from "next/link"; export function RootLayout({ children }: { children: React.ReactNode }) { return (
{children}
); } ``` ```tsx // blog/app/layout.tsx (the same in every zone) import { RootLayout } from "shared/root-layout"; export default RootLayout; ``` ## 4. Follow new versions in open tabs In the shell's root layout, render [``](updates.md) once. Open tabs then pick up a newly installed zone image on their next navigation. ## 5. Check the workspace ```sh npx next-zones check . ``` ``` ✓ / shell ✓ /blog blog ✓ /shop shop ``` It fails if two zones claim one mount, if no zone owns `/`, or if an alias collides. Run it before every build. ## 6. Build and run Give each zone a `version` in its `package.json`, then: ```sh next-zones build # the shell as an app, blog and shop as images, zones.json pinning their versions next-zones start # Zones on port 3000: the shell, with blog and shop installed ``` - **Releasing a zone** is bumping its version, building its image, and installing it on the running Zones: `next-zones build blog`, then `next-zones install blog 1.1.0` (it needs the shell's `endpoints: { admin: true }`). See [build and start](cli.md#build-and-start). - **Each zone still runs on its own** with `next build` and `next start`. - **One Next app instead:** declare `mode: "single"` in the shell's `zoneConfig`; `next-zones build` then builds the workspace as one app, and `next-zones start` runs it with `next start`. No images, no live installs.