Live Preview в Payload CMS
Live Preview позволяет редакторам видеть изменения контента в реальном времени прямо в admin panel, без публикации. Payload отправляет данные черновика во фронтенд через postMessage — Next.js приложение рендерит страницу с этими данными в iframe.
Конфигурация в Payload
// payload.config.ts
export default buildConfig({
admin: {
livePreview: {
// Брейкпоинты для предпросмотра
breakpoints: [
{ label: 'Mobile', name: 'mobile', width: 375, height: 667 },
{ label: 'Tablet', name: 'tablet', width: 768, height: 1024 },
{ label: 'Desktop', name: 'desktop', width: 1440, height: 900 },
],
},
},
})
// collections/Posts.ts — URL для конкретной коллекции
const Posts: CollectionConfig = {
slug: 'posts',
admin: {
livePreview: {
url: ({ data, locale }) => {
const baseURL = process.env.NEXT_PUBLIC_FRONTEND_URL
const slug = data?.slug || 'preview'
const localeParam = locale?.code ? `?locale=${locale.code}` : ''
return `${baseURL}/posts/${slug}${localeParam}`
},
},
},
}
// globals/HomePage.ts
const HomePage: GlobalConfig = {
slug: 'home-page',
admin: {
livePreview: {
url: () => process.env.NEXT_PUBLIC_FRONTEND_URL || 'http://localhost:3000',
},
},
}
Next.js: хук useLivePreview
npm install @payloadcms/live-preview-react
// app/(frontend)/posts/[slug]/page.tsx
import { LivePreviewListener } from './LivePreviewListener'
export default async function PostPage({ params, searchParams }: {
params: { slug: string }
searchParams: { locale?: string }
}) {
const payload = await getPayload({ config })
const { isEnabled } = draftMode()
const result = await payload.find({
collection: 'posts',
where: { slug: { equals: params.slug } },
draft: isEnabled,
overrideAccess: isEnabled,
locale: searchParams.locale as any || 'ru',
})
const post = result.docs[0]
if (!post) notFound()
return (
<>
{isEnabled && <LivePreviewListener initialData={post} />}
<Article post={post} />
</>
)
}
// app/(frontend)/posts/[slug]/LivePreviewListener.tsx
'use client'
import { useLivePreview } from '@payloadcms/live-preview-react'
import { useRouter } from 'next/navigation'
import type { Post } from '@/payload-types'
export const LivePreviewListener = ({ initialData }: { initialData: Post }) => {
const { data } = useLivePreview<Post>({
initialData,
serverURL: process.env.NEXT_PUBLIC_SERVER_URL!,
depth: 2,
})
// Обновить заголовок страницы при изменении данных
if (typeof document !== 'undefined' && data.title) {
document.title = data.title
}
return null // данные обновляются через initialData prop родителя
}
Полноценный компонент с live data
// Компонент, использующий live data напрямую
'use client'
import { useLivePreview } from '@payloadcms/live-preview-react'
import type { Post } from '@/payload-types'
export const PostPreview = ({ initialPost }: { initialPost: Post }) => {
const { data: post } = useLivePreview<Post>({
initialData: initialPost,
serverURL: process.env.NEXT_PUBLIC_SERVER_URL!,
depth: 2,
})
return (
<article>
<h1>{post.title}</h1>
{post.featuredImage && typeof post.featuredImage !== 'string' && (
<img src={post.featuredImage.url!} alt={post.featuredImage.alt} />
)}
<RichText content={post.content} />
</article>
)
}
Включение Draft Mode в Next.js
// app/api/draft/route.ts
import { draftMode } from 'next/headers'
import { NextRequest, NextResponse } from 'next/server'
export async function GET(req: NextRequest) {
const secret = req.nextUrl.searchParams.get('secret')
const slug = req.nextUrl.searchParams.get('slug')
const collection = req.nextUrl.searchParams.get('collection')
if (secret !== process.env.PAYLOAD_DRAFT_SECRET) {
return NextResponse.json({ error: 'Invalid token' }, { status: 401 })
}
draftMode().enable()
const redirectMap: Record<string, string> = {
posts: `/posts/${slug}`,
pages: `/${slug}`,
}
return NextResponse.redirect(
new URL(redirectMap[collection!] || '/', req.url)
)
}
Сроки
Настройка Live Preview для 2–3 типов контента с мобильными и десктопными брейкпоинтами — 1 день.







