--- title: "Admin Preview & Draft Preview" aliases: [payload-preview, draft-preview, payload-draft-mode] tags: [payloadcms, admin, preview, draft, nextjs] sources: [raw/admin__preview.md] created: 2026-05-15 updated: 2026-05-15 --- ## Overview Preview generates a direct link from the Admin Edit View to your front-end app. A **Preview** button appears in the edit view with an `href` pointing to the URL your function returns. > **Not** [[wiki/payloadcms/admin-panel-overview|Live Preview]] — Live Preview embeds your app in an iframe inside the Admin Panel. Preview just navigates to an external URL. ## Setup Add `admin.preview` to any Collection or Global config: ```ts export const Pages: CollectionConfig = { slug: 'pages', admin: { preview: ({ slug }) => `http://localhost:3000/${slug}`, }, } ``` ### Function Signature ```ts preview: (doc, options) => string | null | Promise ``` | Arg | Type | Description | |-----|------|-------------| | `doc` | object | Full document data including unsaved changes | | `options.locale` | string | Current locale | | `options.req` | PayloadRequest | Full request object | | `options.token` | string | JWT of authenticated user | Build absolute URLs using `req`: ```ts preview: (doc, { req }) => `${req.protocol}//${req.host}/${doc.slug}` ``` ## Draft Preview Draft Preview allows editors to see unpublished content. Flow: 1. Admin clicks **Preview** → navigates to a custom endpoint on the front-end 2. Endpoint verifies a shared `PREVIEW_SECRET`, authenticates the user via `payload.auth()` 3. Endpoint enables Next.js Draft Mode (sets a cookie) and redirects to the page 4. Page query includes `draft: true` → Payload returns draft document ### Next.js Implementation (3 Steps) #### Step 1 — Format Preview URL ```ts preview: ({ slug }) => { const params = new URLSearchParams({ slug, collection: 'pages', path: `/${slug}`, previewSecret: process.env.PREVIEW_SECRET || '', }) return `/preview?${params.toString()}` } ``` #### Step 2 — Create `/app/preview/route.ts` ```ts import { draftMode } from 'next/headers' import { redirect } from 'next/navigation' import { getPayload } from 'payload' export async function GET(req) { const payload = await getPayload({ config: configPromise }) const { searchParams } = new URL(req.url) const path = searchParams.get('path') const previewSecret = searchParams.get('previewSecret') if (previewSecret !== process.env.PREVIEW_SECRET) return new Response('Forbidden', { status: 403 }) if (!path?.startsWith('/')) return new Response('Bad path', { status: 400 }) let user try { user = await payload.auth({ req, headers: req.headers }) } catch { return new Response('Forbidden', { status: 403 }) } const draft = await draftMode() if (!user) { draft.disable(); return new Response('Forbidden', { status: 403 }) } draft.enable() redirect(path) } ``` #### Step 3 — Query Draft Content ```ts const { isEnabled: isDraftMode } = await draftMode() const page = await payload.find({ collection: 'pages', draft: isDraftMode, overrideAccess: isDraftMode, where: { slug: { equals: slug } }, limit: 1, })?.then(({ docs }) => docs?.[0]) ``` ## Conditional Preview Button Return `null` to hide the button: ```ts preview: (doc) => doc?.enabled ? `http://localhost:3000/${doc.slug}` : null ``` Useful when you only want preview available for documents meeting certain criteria (e.g. has a slug, is a specific status). ## Key Takeaways - `admin.preview` returns a URL string (or `null` to hide button) — can be async - Preview ≠ Live Preview: Preview is a link, Live Preview is an embedded iframe - Draft Preview requires 3 parts: preview URL → `/preview` route → draft-aware page query - Always guard the preview route with a `PREVIEW_SECRET` env var + `payload.auth()` check - Use `req.protocol + req.host` for absolute URLs (required for Vercel Preview Deployments) - Return `null` conditionally to show/hide the preview button based on document state ## Related - [[wiki/payloadcms/admin-panel-overview|Admin Panel Overview]] - [[wiki/payloadcms/admin-preferences|Admin Preferences]] ## Sources - `raw/admin__preview.md` — compiled from https://payloadcms.com/docs/admin/preview