name: payloadcms-website-3-84-1 description: Payload CMS website template providing production-ready starter for content-driven websites, blogs, and portfolios with Next.js App Router, MongoDB, Lexical editor, SEO optimization, draft/live preview, and search. Use when building personal or enterprise-grade websites requiring publication workflows, layout builders, multi-page sites, or integrating Payload CMS with modern frontend technologies.
Payload CMS Website Template 3.84.1
Overview
The Payload CMS Website Template is the official production-ready starter for building content-driven websites, blogs, and portfolios — from small personal sites to enterprise-grade platforms. It combines Payload 3.x's headless CMS with Next.js 16 App Router, providing a fully integrated backend, admin panel, and frontend in a single deployable application.
The template ships with five pre-configured collections (Pages, Posts, Media, Categories, Users), two globals (Header, Footer), a block-based layout builder, Lexical rich text editor with view override system, draft and live preview workflows, SEO plugin integration, full-text search, URL redirects, form builder with multipart upload support, scheduled publishing via jobs queue, and on-demand Next.js cache revalidation.
Key differentiators from the blank template:
- Layout Builder — composable page sections using blocks (Hero, Content, Media, Call To Action, Archive)
- Publication Workflow — draft/published states with versions, autosave, and scheduled publishing
- Live Preview — real-time preview of drafts in the admin panel with SSR rendering
- On-Demand Revalidation — automatic Next.js cache invalidation on content changes
- SEO Plugin — meta title, description, image, and OG preview from the admin
- Search Plugin — SSR-powered full-text search indexed from posts
- Redirects Plugin — admin-managed URL redirects with proper HTTP status codes
- Form Builder Plugin — configurable contact and feedback forms with multipart upload support
- Nested Docs Plugin — hierarchical categories (e.g., "News > Technology")
New in 3.83/3.84:
- Multipart uploads in form builder plugin for file attachments
- Lexical view override system for custom node rendering in rich text
- definePlugin helper with execution ordering and cross-plugin discovery
- Profiling utilities for performance analysis
- Drizzle uuidv7 support for globally sortable unique identifiers
- Storage composite prefixes for simplified key handling
- Custom collection views with client components
When to Use
- Building a personal or enterprise-grade website, blog, or portfolio
- Creating a content publishing platform with a fully featured publication workflow
- Starting a new Payload project with pre-built collections, layout builder, and plugins
- Implementing draft preview and live preview in a Next.js application
- Setting up SEO-managed pages with admin-controlled meta data
- Adding full-text search to a content-driven website
- Migrating an existing site with URL redirect management
- Building contact forms or feedback forms from the CMS admin
Core Concepts
Template Architecture
The template follows Payload's fullstack pattern — backend (Payload CMS) and frontend (Next.js) share a single instance. The src/app directory is split into two route groups:
(payload)/— Admin panel and API routes (auto-generated by Payload)(frontend)/— Public-facing website pages built with Next.js App Router
Collections
Five collections form the data model:
- Pages — Layout builder enabled, draft/published, SEO tab, slug-based routing at
/{slug}. Home page uses slughome. - Posts — Rich text content with Lexical, hero image, related posts, categories, draft/published, SEO tab. Routed at
/posts/{slug}. - Media — Upload-enabled collection with pre-configured image sizes (thumbnail, square, small, medium, large, xlarge, og), focal point support, folder organization, and caption via rich text.
- Categories — Taxonomy for grouping posts. Supports nesting via the nested-docs plugin. Public read access.
- Users — Auth-enabled collection controlling admin panel access. Name field only beyond email/password.
Globals
Two globals manage site-wide content:
- Header — Navigation items (array of links, max 6 rows). Public read access.
- Footer — Footer navigation items (same structure as header). Public read access.
Both globals have afterChange hooks that trigger on-demand revalidation so the frontend updates immediately.
Layout Builder
Pages use a blocks field for composable layouts. Available blocks:
- Hero — Select group with type options (none, highImpact, mediumImpact, lowImpact), rich text content, link group, and optional media.
- Content — Standard content block with columns and rich text.
- MediaBlock — Renders media from the Media collection with caption support.
- CallToAction — Promotional section with heading, rich text, and links.
- Archive — Lists posts by collection or individual selection, filterable by category with configurable limit.
- Form — Form builder block for embedding forms on pages.
Posts use a different content model — Lexical rich text with inline blocks (Banner, Code, MediaBlock) rather than the page-level layout builder.
Access Control Pattern
The template uses three access control functions:
anyone— Returnstrue(public access, used for Media and Categories read)authenticated— ReturnsBoolean(user)(logged-in users only, used for create/update/delete)authenticatedOrPublished— Returnstruefor logged-in users, otherwise filters by_status: { equals: 'published' }(used for Pages and Posts read)
Plugins
Five official plugins are configured in src/plugins/index.ts:
- redirectsPlugin — Manages URL redirects for pages and posts collections, with afterChange revalidation hook
- nestedDocsPlugin — Enables hierarchical nesting for categories, generates nested URLs from slug chains
- seoPlugin — Provides meta title/description/image fields with generate functions for automatic SEO data
- formBuilderPlugin — Creates form definitions and submissions, with payment disabled, Lexical editor for confirmation messages, and multipart upload support (new in 3.84)
- searchPlugin — Indexes posts for full-text search with beforeSync hook to populate categories and metadata
Project Structure
website-template/
├── src/
│ ├── app/
│ │ └── (frontend)/ # Public website
│ │ ├── [slug]/page.tsx # Dynamic page routes
│ │ ├── posts/[slug]/ # Post routes
│ │ ├── search/ # Search results page
│ │ ├── next/preview/ # Draft preview endpoint
│ │ ├── (sitemaps)/ # XML sitemaps
│ │ ├── layout.tsx # Root layout with Header/Footer/AdminBar
│ │ ├── page.tsx # Home page (delegates to [slug]/page)
│ │ └── not-found.tsx # 404 page
│ │ └── (payload)/ # Payload backend
│ │ ├── admin/ # Auto-generated admin panel
│ │ └── api/ # Auto-generated API routes
│ ├── collections/
│ │ ├── Pages/ # Pages collection + revalidate hooks
│ │ ├── Posts/ # Posts collection + revalidate hooks
│ │ ├── Users/ # Auth-enabled users
│ │ ├── Media.ts # Upload collection with image sizes
│ │ └── Categories.ts # Nested taxonomy
│ ├── globals/ # (Header, Footer defined as modules)
│ ├── blocks/ # Layout builder blocks
│ │ ├── ArchiveBlock/
│ │ ├── Banner/
│ │ ├── CallToAction/
│ │ ├── Code/
│ │ ├── Content/
│ │ ├── Form/
│ │ ├── MediaBlock/
│ │ ├── RelatedPosts/
│ │ └── RenderBlocks.tsx # Block dispatcher component
│ ├── heros/ # Hero components (high, medium, low impact)
│ ├── fields/ # Reusable field definitions (link, linkGroup)
│ ├── access/ # Access control functions
│ ├── hooks/ # Shared hooks (revalidateRedirects, etc.)
│ ├── plugins/ # Plugin configuration
│ ├── search/ # Search plugin overrides and sync logic
│ ├── components/ # Shared React components
│ ├── providers/ # React context providers
│ ├── utilities/ # Helper functions (getURL, deepMerge, etc.)
│ ├── endpoints/ # Seed data endpoints
│ ├── payload.config.ts # Central Payload configuration
│ └── payload-types.ts # Auto-generated TypeScript types
├── public/media/ # Static file upload directory
├── next.config.ts # Next.js config with withPayload wrapper
├── redirects.ts # Next.js redirect definitions (IE fallback)
├── tailwind.config.mjs # Tailwind CSS configuration
├── tsconfig.json
├── Dockerfile # Multi-stage Docker build
└── docker-compose.yml # Docker Compose with MongoDB
Installation / Setup
Quick Start
Use the create-payload-app CLI to scaffold the template:
pnpx create-payload-app my-project -t website
cd my-project
cp .env.example .env
pnpm install && pnpm dev
Open http://localhost:3000 and follow on-screen instructions to create the first admin user.
Environment Variables
Key variables from .env.example:
PAYLOAD_SECRET— Encryption secret for cookies and JWTDATABASE_URL— MongoDB connection stringPREVIEW_SECRET— Secret for draft preview authenticationCRON_SECRET— Authorization bearer token for scheduled jobsNEXT_PUBLIC_SERVER_URL— Public URL of the applicationVERCEL_PROJECT_PRODUCTION_URL— Vercel deployment hostname (auto-detected)
Database Options
The template defaults to MongoDB via @payloadcms/db-mongodb. For Postgres on Vercel:
import { vercelPostgresAdapter } from '@payloadcms/db-vercel-postgres'
db: vercelPostgresAdapter({
pool: {
connectionString: process.env.POSTGRES_URL || '',
},
}),
For Postgres with migrations, run pnpm payload migrate:create locally and pnpm payload migrate on deploy. Set push: false for production databases.
Seed Data
Click "seed database" from the admin panel to populate Pages, Posts, and Categories with sample content. This also creates a demo author account (demo-author@payloadcms.com / password). Seeding drops and recreates the database — use only on fresh projects.
Usage Examples
Payload Config
// src/payload.config.ts
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import sharp from 'sharp'
import path from 'path'
import { buildConfig } from 'payload'
import { fileURLToPath } from 'url'
import { Categories } from './collections/Categories'
import { Media } from './collections/Media'
import { Pages } from './collections/Pages'
import { Posts } from './collections/Posts'
import { Users } from './collections/Users'
import { Footer } from './Footer/config'
import { Header } from './Header/config'
import { plugins } from './plugins'
import { defaultLexical } from '@/fields/defaultLexical'
import { getServerSideURL } from './utilities/getURL'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
admin: {
user: Users.slug,
importMap: { baseDir: path.resolve(dirname) },
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: [Pages, Posts, Media, Categories, Users],
cors: [getServerSideURL()].filter(Boolean),
db: mongooseAdapter({ url: process.env.DATABASE_URL }),
editor: defaultLexical,
globals: [Header, Footer],
plugins,
secret: process.env.PAYLOAD_SECRET,
sharp,
typescript: { outputFile: path.resolve(dirname, 'payload-types.ts') },
jobs: {
access: {
run: ({ req }) => {
if (req.user) return true
const secret = process.env.CRON_SECRET
if (!secret) return false
return req.headers.get('authorization') === `Bearer ${secret}`
},
},
tasks: [],
},
})
Pages Collection with Drafts and Revalidation
// src/collections/Pages/index.ts
export const Pages: CollectionConfig<'pages'> = {
slug: 'pages',
access: {
create: authenticated,
delete: authenticated,
read: authenticatedOrPublished,
update: authenticated,
},
admin: {
useAsTitle: 'title',
preview: (data, { req }) =>
generatePreviewPath({ slug: data?.slug as string, collection: 'pages', req }),
livePreview: {
url: ({ data, req }) =>
generatePreviewPath({ slug: data?.slug, collection: 'pages', req }),
},
},
fields: [
{ name: 'title', type: 'text', required: true },
{
type: 'tabs',
tabs: [
{ label: 'Hero', fields: [hero] },
{
label: 'Content',
fields: [{
name: 'layout',
type: 'blocks',
blocks: [CallToAction, Content, MediaBlock, Archive, FormBlock],
required: true,
}],
},
{
name: 'meta',
label: 'SEO',
fields: [
OverviewField({ titlePath: 'meta.title', descriptionPath: 'meta.description', imagePath: 'meta.image' }),
MetaTitleField({ hasGenerateFn: true }),
MetaImageField({ relationTo: 'media' }),
MetaDescriptionField({}),
PreviewField({ hasGenerateFn: true, titlePath: 'meta.title', descriptionPath: 'meta.description' }),
],
},
],
},
{ name: 'publishedAt', type: 'date', admin: { position: 'sidebar' } },
slugField(),
],
hooks: {
afterChange: [revalidatePage],
beforeChange: [populatePublishedAt],
afterDelete: [revalidateDelete],
},
versions: {
drafts: {
autosave: { interval: 100 },
schedulePublish: true,
},
maxPerDoc: 50,
},
}
On-Demand Revalidation Hook
// src/collections/Pages/hooks/revalidatePage.ts
import type { CollectionAfterChangeHook } from 'payload'
import { revalidatePath, revalidateTag } from 'next/cache'
import type { Page } from '../../../payload-types'
export const revalidatePage: CollectionAfterChangeHook<Page> = ({
doc,
previousDoc,
req: { payload, context },
}) => {
if (!context.disableRevalidate) {
if (doc._status === 'published') {
const path = doc.slug === 'home' ? '/' : `/${doc.slug}`
payload.logger.info(`Revalidating page at path: ${path}`)
revalidatePath(path)
revalidateTag('pages-sitemap', 'max')
}
if (previousDoc?._status === 'published' && doc._status !== 'published') {
const oldPath = previousDoc.slug === 'home' ? '/' : `/${previousDoc.slug}`
revalidatePath(oldPath)
revalidateTag('pages-sitemap', 'max')
}
}
return doc
}
Frontend Page with Draft Mode and Local API
// src/app/(frontend)/[slug]/page.tsx
import { getPayload } from 'payload'
import configPromise from '@payload-config'
import { draftMode } from 'next/headers'
import { cache } from 'react'
import { RenderBlocks } from '@/blocks/RenderBlocks'
import { RenderHero } from '@/heros/RenderHero'
import { PayloadRedirects } from '@/components/PayloadRedirects'
import { LivePreviewListener } from '@/components/LivePreviewListener'
export default async function Page({ params: paramsPromise }: { params: Promise<{ slug?: string }> }) {
const { isEnabled: draft } = await draftMode()
const { slug = 'home' } = await paramsPromise
const decodedSlug = decodeURIComponent(slug)
const page = await queryPageBySlug({ slug: decodedSlug })
if (!page) return <PayloadRedirects url={'/' + decodedSlug} />
return (
<article className="pt-16 pb-24">
<PayloadRedirects disableNotFound url={'/' + decodedSlug} />
{draft && <LivePreviewListener />}
<RenderHero {...page.hero} />
<RenderBlocks blocks={page.layout} />
</article>
)
}
const queryPageBySlug = cache(async ({ slug }: { slug: string }) => {
const { isEnabled: draft } = await draftMode()
const payload = await getPayload({ config: configPromise })
const result = await payload.find({
collection: 'pages',
draft,
limit: 1,
pagination: false,
overrideAccess: draft,
where: { slug: { equals: slug } },
})
return result.docs?.[0] || null
})
Static Params Generation for ISR
export async function generateStaticParams() {
const payload = await getPayload({ config: configPromise })
const pages = await payload.find({
collection: 'pages',
draft: false,
limit: 1000,
overrideAccess: false,
pagination: false,
select: { slug: true },
})
return pages.docs
?.filter((doc) => doc.slug !== 'home')
.map(({ slug }) => ({ slug }))
}
Advanced Topics
Collections and Data Model: Complete reference for Pages, Posts, Media, Categories, and Users collections including field definitions, access control, hooks, and versioning configuration → Collections and Data Model
Layout Builder and Blocks: Block-based page composition with Hero types, Content columns, Archive filtering, Form embedding, and the RenderBlocks dispatcher pattern → Layout Builder and Blocks
Plugins Configuration: Detailed setup for SEO, Search, Redirects, Form Builder with multipart uploads, and Nested Docs plugins including field overrides, sync hooks, and generate functions → Plugins Configuration
Frontend Architecture: Next.js App Router patterns including draft mode integration, React cache for queries, static params generation, metadata API, sitemaps, and the frontend layout structure → Frontend Architecture
Draft Preview and Live Preview: Draft preview URL generation, preview endpoint routing, live preview with SSR breakpoints, autosave configuration, and scheduled publishing → Draft Preview and Live Preview
Deployment and Production: Vercel deployment with Postgres adapter, self-hosting patterns, Docker multi-stage builds, cache strategies, cron configuration for scheduled jobs, and migration management → Deployment and Production