---
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
