# Payloadcms Website 3 82 1

> 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.

- Skill: `tangledgroup/payloadcms-website-3-82-1` (Agent Skill, multi-file: 7 files)
- Install (CLI): `npx skillmds@latest add tangledgroup/payloadcms-website-3-82-1`
- Raw SKILL.md: https://api.skillmd.com/api/skills/tangledgroup/payloadcms-website-3-82-1/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Marketing & Growth
- License: MIT
- Author: tangledgroup (https://skillmd.com/u/tangledgroup)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/tangledgroup/payloadcms-website-3-82-1

---


# Payload CMS Website Template 3.82.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, draft and live preview workflows, SEO plugin integration, full-text search, URL redirects, form builder, 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
- **Nested Docs Plugin** — hierarchical categories (e.g., "News > Technology")

## 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 slug `home`.
- **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` — Returns `true` (public access, used for Media and Categories read)
- `authenticated` — Returns `Boolean(user)` (logged-in users only, used for create/update/delete)
- `authenticatedOrPublished` — Returns `true` for 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`:

1. **redirectsPlugin** — Manages URL redirects for pages and posts collections, with afterChange revalidation hook
2. **nestedDocsPlugin** — Enables hierarchical nesting for categories, generates nested URLs from slug chains
3. **seoPlugin** — Provides meta title/description/image fields with generate functions for automatic SEO data
4. **formBuilderPlugin** — Creates form definitions and submissions, with payment disabled and Lexical editor for confirmation messages
5. **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:

```bash
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 JWT
- `DATABASE_URL` — MongoDB connection string
- `PREVIEW_SECRET` — Secret for draft preview authentication
- `CRON_SECRET` — Authorization bearer token for scheduled jobs
- `NEXT_PUBLIC_SERVER_URL` — Public URL of the application
- `VERCEL_PROJECT_PRODUCTION_URL` — Vercel deployment hostname (auto-detected)

### Database Options

The template defaults to MongoDB via `@payloadcms/db-mongodb`. For Postgres on Vercel:

```typescript
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

```typescript
// 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

```typescript
// 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

```typescript
// 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

```typescript
// 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

```typescript
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](reference/01-collections-and-data-model.md)

**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](reference/02-layout-builder-and-blocks.md)

**Plugins Configuration**: Detailed setup for SEO, Search, Redirects, Form Builder, and Nested Docs plugins including field overrides, sync hooks, and generate functions → [Plugins Configuration](reference/03-plugins-configuration.md)

**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](reference/04-frontend-architecture.md)

**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](reference/05-draft-preview-and-live-preview.md)

**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](reference/06-deployment-and-production.md)

