All Blog Articles

Creating dynamic pages with Payload & NextJS

Media
Sandro WegmannAugust 6, 2024
Payload V3 Changes Thumbnail

This is the written guide for our dynamic website tutorial series (Part 3). You can find the full length video here: https://youtu.be/8BXT64lnTXc

--- This guide is WIP and will be extended & improved ---

Root Layout

https://nextjs.org/docs/app/building-your-application/upgrading/app-router-migration#step-2-creating-a-root-layout


TailwindCSS Setup

https://tailwindcss.com/docs/guides/nextjs


Website Layout


import React, { ReactNode } from 'react'

export default function layout({children}: {children: ReactNode}) {
  return (
    <div>
        {/* Header */}
      {children}
      {/* Footer */}
    </div>
  )
}


[slug]/page.tsx file


import type { Metadata } from 'next'

import config from '@payload-config'
import { getPayloadHMR } from '@payloadcms/next/utilities'
import React, { cache } from 'react'

import type { Page as PageType } from '../../../payload-types'

import { Blocks } from '@/utils/RenderBlocks'
import { generateMeta } from '@/utils/generateMeta'
import { notFound } from 'next/navigation'

const queryPageBySlug = cache(async ({ slug }: { slug: string }) => {

    const parsedSlug = decodeURIComponent(slug)
  
    const payload = await getPayloadHMR({ config })
  
    const result = await payload.find({
      collection: 'pages',
      limit: 1,
      where: {
        slug: {
          equals: parsedSlug,
        },
      },
    })
  
    return result.docs?.[0] || null
  })


export async function generateStaticParams() {
  const payload = await getPayloadHMR({ config })
  const pages = await payload.find({
    collection: 'pages',
    draft: false,
    limit: 1000,
  })

  return pages.docs
    ?.filter((doc) => {
      return doc.slug !== 'index'
    })
    .map(({ slug }) => slug)
}


export default async function Page({ params: { slug = 'index' } }) {
  let page: PageType | null

  page = await queryPageBySlug({
    slug,
  })

  if (!page) {
    return notFound()
  }

  return (
    <article className="pt-16 pb-24">

      <Blocks blocks={page.layout} />
    </article>
  )
}



RenderBlocks


import { Page } from '@/payload-types'
import React, { Fragment } from 'react'



const blockComponents = {

}

export const RenderBlocks: React.FC<{
  blocks: Page['layout'][0][]
}> = (props) => {
  const { blocks } = props

  const hasBlocks = blocks && Array.isArray(blocks) && blocks.length > 0

  if (hasBlocks) {
    return (
      <Fragment>
        {blocks.map((block, index) => {
          const { blockName, blockType } = block

          if (blockType && blockType in blockComponents) {
            const Block = blockComponents[blockType]

            if (Block) {
              return (
                <div className="my-16" key={index}>
                  <Block id={blockName} {...block} />
                </div>
              )
            }
          }
          return null
        })}
      </Fragment>
    )
  }

  return null
}


RichText

You can find the RichText parser from the official Payload Website Template here:

https://github.com/payloadcms/payload/templates/website/src/app/components/RichText/serialize.tsx