---
title: "Live preview"
description: "Server and client modes, how preview works, and local builds."
requested_language: ar
language: en
translation_notice: "This page isn't translated yet"
url: https://docs.systhema.app/ar/payload/frontend/live-preview
version: unreleased (main)
docs_index: https://docs.systhema.app/ar/llms.txt
---
> This page isn't translated yet. Showing English.


Live Preview places the page beside its Admin editor.

![A page editor beside its live preview](./images/live-preview.light.webp)

Systhema wires up Payload [Live Preview](https://payloadcms.com/docs/live-preview/overview) for Pages, Components, and Posts (when the Posts module is enabled) out of the box — editing a document shows the rendered page in a resizable iframe inside the admin. There are two rendering modes, chosen with the `livePreview` plugin option:

```ts
withSysthema(payloadConfig, {
  livePreview: {
    mode: 'server', // 'server' (default) | 'client'
    depth: 2,       // relationship-population depth for client mode
  },
})
```

|                         | `server` (default)                                                                                                 | `client`                                                                                                        |
| ----------------------- | ------------------------------------------------------------------------------------------------------------------ | --------------------------------------------------------------------------------------------------------------- |
| Mechanism               | Next.js **draft mode** + `RefreshRouteOnSave` — re-fetches the autosaved draft and re-renders the server component | `useLivePreview` — receives the editor's **unsaved** form state via `postMessage` and re-renders in the browser |
| Updates after           | the autosave debounce (~1s)                                                                                        | every keystroke, in real time                                                                                   |
| Fidelity                | full — the exact server render, all relationships and server-only logic                                            | high — re-populates relationships to `depth`; a few server-only niceties degrade in the preview (see below)     |
| Needs a save / DB write | yes (autosave)                                                                                                     | no                                                                                                              |

New projects scaffolded from the Payload template (`systhema create`, `systhema-core payload create-config`) set `mode: 'client'` explicitly. Projects without the option keep the `server` default.

Both modes leave **public (published) pages statically generated** — the mode only affects what the editor sees in the preview iframe.

## How it works

The preview iframe and the page render are wired by a few pieces that you mostly don't touch:

1. **Entry — the collection's `admin.livePreview.url`.** Built by `systhemaPreviewAdmin()` (or `generatePreviewPath()`), it points the iframe at `/sys/preview?collection=…&id=…&locale=…&path=/your/route&livePreview=true`. The persisted document ID remains stable while an editor changes a localized slug or post category.
2. **`/sys/preview` route** (mounted at `app/(site)/(systhema)/sys/[route]/route.ts`). It validates the preview secret and explicit locale, authenticates the editor, enables Next.js draft mode, **relaxes the draft cookie on a localhost http build** (see below), and redirects to the route with its collection, stable ID, locale, and `?__livePreview=true`. This route is collection-agnostic — it works for built-in _and_ custom collections.
3. **The page render — `<SysthemaLivePreview>`.** Your route fetches the doc (respecting `draftMode()`) and wraps the render. The wrapper reads `livePreview.mode` on the server and picks the mechanism:
   - **server mode** → renders your view; when the editor is previewing, it also mounts `LivePreviewListener` (Payload's `RefreshRouteOnSave`). On each autosave the admin posts a message, the listener calls `router.refresh()`, and the server component re-fetches the draft.
   - **client mode** → renders a tiny client **gate**. Public visitors get the rendered view unchanged (the route stays SSG). Inside the iframe the gate detects `__livePreview` _client-side_ (via `window.location`, robust on static routes) and lazy-loads a renderer that calls `useLivePreview`: the admin streams the editor's unsaved form state over `postMessage`, the hook re-populates relationships via the REST API to `depth`, and your `view` (or `clientView`) re-renders — no draft cookie, no DB write.

With frontend locales enabled, the active Payload content locale is part of the preview URL and public path. The `/sys/preview` entry itself stays unprefixed; after authentication it redirects to the locale-aware catch-all path. The explicit preview locale wins even when the default locale uses unprefixed URLs, so draft fetching, live updates, the shell catalog, links, and metadata all use the language currently selected in the editor.

The built-in Pages view uses exactly this wrapper, and the Pages/Components admin blocks use `systhemaPreviewAdmin()` — so the public API and the framework's own collections are the same code path. To wire your **own** collection, see [Your own collections](https://docs.systhema.app/ar/payload/frontend/live-preview/custom-collections.md).

On localized sites, the shared `@systhemaui/next/gateway` contract makes the callback's explicit locale authoritative for both Preview and Live Preview. A stale `NEXT_LOCALE` cookie cannot move the iframe to another language. Prefix routing follows the project's normal `as-needed` or `always` policy and can keep a relative iframe on the custom Admin origin; domain routing selects the explicit locale's canonical public origin. Query parameters are preserved in either mode. Built-in page and post renders resolve the stable ID in the exact locale first, then reload display fields with Payload's configured fallback chain; post category slugs remain strict route keys.

With locale domains, mapped public hosts use unprefixed paths and a wrong locale prefix redirects to that locale's canonical host. The explicit Preview locale and every Preview query parameter survive that cross-host redirect. The same domain map drives canonical and alternate metadata, sitemap entries, and Preview/Live Preview targets. The generated route tree and `/sys/preview` entry do not move or multiply.

## Local builds and the draft-mode cookie

Server-side live preview relies on the Next.js draft-mode cookie, which a production build sets as `Secure; SameSite=None`. Over plain `http://localhost` that `Secure` cookie is honored by Chrome/Firefox but **rejected by Safari/WebKit**, and as a `SameSite=None` third-party cookie it is blocked across a cross-origin admin (`customAdminURL`) in every browser.

To keep `pnpm build && pnpm start` usable, Systhema's preview route **automatically re-sets that cookie as `SameSite=Lax` (no `Secure`) when, and only when, the request is a loopback host (`localhost`/`127.0.0.1`/`::1`) reached over plain http** — so server mode live-updates on a locally built **same-origin** site in every browser. HTTPS deployments (and anything behind a TLS-terminating proxy) are never touched.

The one case this can't cover is a **cross-origin admin** (`customAdminURL`) over plain http: a third-party cookie fundamentally needs `SameSite=None; Secure`, i.e. TLS. There, use `client` mode (or local HTTPS):

|          | `pnpm dev` | deployed (HTTPS) | local build, same-origin | local build, cross-origin admin |
| -------- | ---------- | ---------------- | ------------------------ | ------------------------------- |
| `server` | ✅         | ✅               | ✅ (auto cookie relax)   | ❌ → use `client`               |
| `client` | ✅         | ✅               | ✅                       | ✅                              |

The cross-origin `client` cells assume the Admin and iframe origins are allowed by the deployment configuration described below.
