Інтеграція 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/[[...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

У серверних компонентах можна викликати 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 })
}

Клієнтські операції

Автентифікація та операції, які вимагають токена, обробляються через 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 дні з існуючими колекціями.