Payload Dynamic Websites Part 3
Sandro Wegmann
August 6, 2024
Media

Creating dynamic pages with Payload & NextJS

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

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: