Интеграция Payload CMS с Next.js фронтендом

Наша компания занимается разработкой, поддержкой и обслуживанием сайтов любой сложности. От простых одностраничных сайтов до масштабных кластерных систем построенных на микро сервисах. Опыт разработчиков подтвержден сертификатами от вендоров.

Разработка и обслуживание любых видов сайтов:

Информационные сайты или веб-приложения
Сайты визитки, landing page, корпоративные сайты, онлайн каталоги, квиз, промо-сайты, блоги, новостные ресурсы, информационные порталы, форумы, агрегаторы
Сайты или веб-приложения электронной коммерции
Интернет-магазины, B2B-порталы, маркетплейсы, онлайн-обменники, кэшбэк-сайты, биржи, дропшиппинг-платформы, парсеры товаров
Веб-приложения для управления бизнес-процессами
CRM-системы, ERP-системы, корпоративные порталы, системы управления производством, парсеры информации
Сайты или веб-приложения электронных услуг
Доски объявлений, онлайн-школы, онлайн-кинотеатры, конструкторы сайтов, порталы предоставления электронных услуг, видеохостинги, тематические порталы

Это лишь некоторые из технических типов сайтов, с которыми мы работаем, и каждый из них может иметь свои специфические особенности и функциональность, а также быть адаптированным под конкретные потребности и цели клиента

Предлагаемые услуги
Показано 1 из 1 услугВсе 2065 услуг
Интеграция Payload CMS с Next.js фронтендом
Средняя
~3-5 рабочих дней
Часто задаваемые вопросы

Наши компетенции:

Этапы разработки

Последние работы

  • image_website-b2b-advance_0.png
    Разработка сайта компании B2B ADVANCE
    1262
  • image_web-applications_feedme_466_0.webp
    Разработка веб-приложения для компании FEEDME
    1171
  • image_websites_belfingroup_462_0.webp
    Разработка веб-сайта для компании БЕЛФИНГРУПП
    874
  • image_ecommerce_furnoro_435_0.webp
    Разработка интернет магазина для компании FURNORO
    1094
  • image_crm_enviok_479_0.webp
    Разработка веб-приложения для компании Enviok
    831
  • image_bitrix-bitrix-24-1c_fixper_448_0.png
    Разработка веб-сайта для компании ФИКСПЕР
    851

Интеграция Payload CMS с Next.js

Payload 2.x создан с расчётом на интеграцию с Next.js App Router. Монолитный подход — обе системы в одном процессе — исключает сетевые запросы между CMS и фронтендом при серверном рендеринге. Это главное отличие от других headless CMS.

Монолитная архитектура

npx create-payload-app@latest --template website

Структура Next.js + Payload монолита:

my-app/
├── app/
│   ├── (frontend)/           # Публичный сайт
│   │   ├── layout.tsx
│   │   ├── page.tsx
│   │   └── [slug]/page.tsx
│   └── (payload)/            # Admin panel
│       └── admin/[[...segments]]/page.tsx
├── collections/
├── globals/
├── payload.config.ts
└── next.config.js
// next.config.js
const { withPayload } = require('@payloadcms/next/withPayload')

module.exports = withPayload({
  // ваши настройки Next.js
  images: {
    remotePatterns: [{ hostname: 'your-cdn.com' }],
  },
})

Прямые запросы без HTTP

В Server Components можно вызывать Payload напрямую — без HTTP:

// app/(frontend)/page.tsx
import { getPayload } from 'payload'
import config from '@payload-config'

export default async function HomePage() {
  const payload = await getPayload({ config })

  // Прямой вызов — нет сетевого запроса
  const [homepage, posts, settings] = await Promise.all([
    payload.findGlobal({ slug: 'homepage', depth: 2 }),
    payload.find({
      collection: 'posts',
      where: { _status: { equals: 'published' } },
      sort: '-publishedAt',
      limit: 6,
      depth: 1,
    }),
    payload.findGlobal({ slug: 'settings' }),
  ])

  return (
    <>
      <Hero data={homepage.hero} />
      <FeaturedPosts posts={posts.docs} />
      <Footer settings={settings} />
    </>
  )
}

ISR — Incremental Static Regeneration

// app/(frontend)/posts/[slug]/page.tsx
import { unstable_cache } from 'next/cache'

// Кэшировать запрос с тегом для инвалидации
const getCachedPost = unstable_cache(
  async (slug: string) => {
    const payload = await getPayload({ config })
    const result = await payload.find({
      collection: 'posts',
      where: { slug: { equals: slug }, _status: { equals: 'published' } },
    })
    return result.docs[0] || null
  },
  ['post'],
  { tags: ['posts'], revalidate: 3600 }
)

export default async function PostPage({ params }: { params: { slug: string } }) {
  const post = await getCachedPost(params.slug)
  if (!post) notFound()
  return <Article post={post} />
}

On-demand Revalidation через хуки Payload

// collections/Posts.ts — инвалидация при изменении
hooks: {
  afterChange: [
    async ({ doc, operation }) => {
      if (doc._status === 'published') {
        // Инвалидировать кэш по тегу
        await revalidateTag('posts')
        // Инвалидировать конкретную страницу
        await revalidatePath(`/posts/${doc.slug}`)
      }
    },
  ],
}
// app/api/revalidate/route.ts
import { revalidatePath, revalidateTag } from 'next/cache'
import { NextRequest, NextResponse } from 'next/server'

export async function POST(req: NextRequest) {
  const secret = req.headers.get('x-revalidate-secret')
  if (secret !== process.env.REVALIDATE_SECRET) {
    return NextResponse.json({ error: 'Unauthorized' }, { status: 401 })
  }

  const { path, tag } = await req.json()
  if (tag) revalidateTag(tag)
  if (path) revalidatePath(path)

  return NextResponse.json({ revalidated: true })
}

Client-side операции

Авторизация и операции, требующие токена, — через fetch в Client Components:

// app/(frontend)/components/ContactForm.tsx
'use client'
import { useState } from 'react'

export const ContactForm = () => {
  const [status, setStatus] = useState<'idle' | 'loading' | 'success' | 'error'>('idle')

  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
    e.preventDefault()
    setStatus('loading')
    const formData = new FormData(e.currentTarget)

    const res = await fetch('/api/contact', {
      method: 'POST',
      headers: { 'Content-Type': 'application/json' },
      body: JSON.stringify(Object.fromEntries(formData)),
    })

    setStatus(res.ok ? 'success' : 'error')
  }

  return (
    <form onSubmit={handleSubmit}>
      {/* поля формы */}
      <button disabled={status === 'loading'}>
        {status === 'loading' ? 'Отправка...' : 'Отправить'}
      </button>
      {status === 'success' && <p>Заявка отправлена!</p>}
    </form>
  )
}

TypeScript: автогенерируемые типы

После изменения коллекций — перегенерировать типы:

npm run generate:types
// Использование типов
import type { Post, Page, Media, Settings } from '@/payload-types'

// Полная типизация ответов API
const posts: Post[] = result.docs
const settings: Settings = await payload.findGlobal({ slug: 'settings' })

Сроки

Интеграция Payload с Next.js App Router, настройка ISR, типизация — 2–3 дня при работе с готовыми коллекциями.