> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stage.systems/llms.txt
> Use this file to discover all available pages before exploring further.

# Next.js sites

> Stage on Next.js: the same packages and the same contract, plus an adapter that supplies the React anchors, the admin mount, and a zero-JS edit canvas.

A Next.js site consumes `@sp-stage/sdk` and `@sp-stage/admin` exactly like an Astro site. The third package, **`@sp-stage/next`**, supplies the Next-shaped glue: the anchor helpers as React props, `<StageAdmin>` for the `/admin` route, renderers for pages and article bodies, the media slot, the preview canvas resolvers, and the on-demand publish handler. Everything on the other Developers pages applies; this section covers what differs.

## The one design decision

The admin edits by writing directly into the canvas frame's DOM: contenteditable text, grafted adds. Hydrated React would clobber those writes. So a Next site is split:

| Surface                        | Router                 | Ships JavaScript             | Why                                                                                                                                                          |
| ------------------------------ | ---------------------- | ---------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| Public pages                   | App Router             | Yes, hydrated, animated      | Ordinary server components. The editor never touches them                                                                                                    |
| The edit canvas, `/preview/*`  | Pages Router, one file | **No**, in production builds | `export const config = { unstable_runtimeJS: false }` is pages-router-only. The served HTML has no script tags, structurally the same as a static Astro page |
| The browse canvas, `/browse/*` | App Router             | Yes                          | The hydrated live site, gated. The admin browses here and swaps to the inert canvas only while editing                                                       |
| `/admin`                       | App Router             | Yes                          | A server shell for metadata plus a client component that mounts the admin                                                                                    |

The routers coexist natively. The canvas is an internal editor surface no visitor sees. A pure pages-router site works too; every pages-shaped helper remains exported.

<Warning>
  `next dev` ignores `unstable_runtimeJS`, so dev canvases hydrate and can look fine while a production canvas is broken. Verify editing against a production build: `next build && next start`.
</Warning>

## Zero JS means: anything a script was going to supply is missing

Two ways a canvas silently renders wrong. Check both on every route you add.

<Steps>
  <Step title="Data a client effect seeds">
    A component driven by a store that a `useEffect` fills renders nothing on the canvas. Pass the data in as props instead. `PageRenderer` and `ArticleBody` spread a `context` prop into every renderer for exactly this. The article canvas is the easy one to miss: wrap the article view in the site's own shell and hand it the globals, or posts render with no footer while every page has one.
  </Step>

  <Step title="State a script was going to reveal">
    An image that fades in from `opacity-0` in an `onLoad` handler stays transparent. A motion library's `initial={{ opacity: 0 }}` is inlined as a style and never animates. Reveal them with canvas-scoped CSS (`.stage-canvas img.opacity-0.transition-opacity { opacity: 1 }`) rather than per component. Scope tightly: overlays meant to stay hidden until JS runs must stay hidden.
  </Step>
</Steps>

## The three laws

1. **No time-based ISR on Stage pages.** Stage's semantic is *live equals last publish*. `export const revalidate = N` would regenerate in the background and leak saved-but-unpublished edits. Only the publish handler regenerates.
2. **The canvas is zero-JS.** Components render complete without scripts. Anything a script reveals needs a canvas-scoped CSS rule or a props-passed data path.
3. **`/admin` loads zero site CSS.** Site styles enter through the site layout group, the browse canvas and the pages `_app`, never the root layout. An unlayered Tailwind preflight beats the admin's layered design system regardless of order, and every admin button flattens to transparent.

## Requirements

* Next 15 or later. Site routes in the App Router; the canvas as one pages-router file.
* **One React 19 everywhere.** The admin shell is React 19; a React 18 host breaks it with an invalid hook call.
* `transpilePackages: ["@sp-stage/admin", "@sp-stage/sdk", "@sp-stage/next"]` in the Next config.
* Same origin for `/admin`, `/preview/*`, `/browse/*` and the site.
* The admin shell's two font files copied once from the package into `public/fonts/`.

## The template

The Next client template is a minimal App Router site plus the pages-router canvas, a reference `Hero`, a site shell that takes globals as props, the blog, the browse canvas, the admin route with a reference schema, and the publish handler. Start a new Next site by cloning and renaming it. Its checklist:

1. Rename the package and replace workspace dependencies with exact-pinned registry versions of the three packages.
2. Delete the in-repo source aliases from the Next config and `tsconfig`.
3. Create the org, fill the env.
4. Seed pages and globals, or migrate from an existing CMS.
5. Rebrand the tokens, build the components, register each type, grow the schema.
6. Keep the blog base path identical in three places: the schema, the browse canvas, and the `pages/preview/<base>/` directory (plus `expandPaths` in the publish handler).
7. Run the canvas sweep on every route: footer present, nothing stuck at opacity 0.
8. Deploy, then flip the org to revalidate publish mode with its secret.

## Next steps

<CardGroup cols={2}>
  <Card title="Wiring" href="/developers/next/wiring">
    Every file, with the code.
  </Card>

  <Card title="Edit anchors" href="/developers/editing/anchors">
    The same attributes, as React props.
  </Card>
</CardGroup>
