obsidian/wiki/payloadcms/admin-preview.md
2026-05-15 15:13:56 +01:00

145 lines
4.2 KiB
Markdown

---
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<string | null>
```
| 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