Payload CMS Application Development
Payload is a Next.js native CMS with TypeScript-first architecture, providing admin panel, database management, REST/GraphQL APIs, authentication, and file storage.
Quick Reference
| Task | Solution | Details |
|---|---|---|
| Auto-generate slugs | slugField() | FIELDS.md#slug-field-helper |
| Restrict content by user | Access control with query | ACCESS-CONTROL.md#row-level-security-with-complex-queries |
| Local API user ops | user + overrideAccess: false | QUERIES.md#access-control-in-local-api |
| Draft/publish workflow | versions: { drafts: true } | COLLECTIONS.md#versioning--drafts |
| Computed fields | virtual: true with afterRead | FIELDS.md#virtual-fields |
| Conditional fields | admin.condition | FIELDS.md#conditional-fields |
| Custom field validation | validate function | FIELDS.md#validation |
| Filter relationship list | filterOptions on field | FIELDS.md#relationship |
| Select specific fields | select parameter | QUERIES.md#field-selection |
| Auto-set author/dates | beforeChange hook | HOOKS.md#collection-hooks |
| Prevent hook loops | req.context check | HOOKS.md#context |
| Cascading deletes | beforeDelete hook | HOOKS.md#collection-hooks |
| Geospatial queries | point field with near/within | FIELDS.md#point-geolocation |
| Reverse relationships | join field type | FIELDS.md#join-fields |
| Next.js revalidation | Context control in afterChange | HOOKS.md#nextjs-revalidation-with-context-control |
| Query by relationship | Nested property syntax | QUERIES.md#nested-properties |
| Complex queries | AND/OR logic | QUERIES.md#andor-logic |
| Transactions | Pass req to operations | ADAPTERS.md#threading-req-through-operations |
| Background jobs | Jobs queue with tasks | ADVANCED.md#jobs-queue |
| Custom API routes | Collection custom endpoints | ADVANCED.md#custom-endpoints |
| Cloud storage | Storage adapter plugins | ADAPTERS.md#storage-adapters |
| Multi-language | localization config + localized: true | ADVANCED.md#localization |
| Create plugin | (options) => (config) => Config | PLUGIN-DEVELOPMENT.md#plugin-architecture |
| Plugin package setup | Package structure with SWC | PLUGIN-DEVELOPMENT.md#plugin-package-structure |
| Add fields to collection | Map collections, spread fields | PLUGIN-DEVELOPMENT.md#adding-fields-to-collections |
| Plugin hooks | Preserve existing hooks in array | PLUGIN-DEVELOPMENT.md#adding-hooks |
| Check field type | Type guard functions | FIELD-TYPE-GUARDS.md |
Quick Start
npx create-payload-app@latest my-app
cd my-app
pnpm dev
Minimal Config
import { buildConfig } from 'payload'
import { mongooseAdapter } from '@payloadcms/db-mongodb'
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import path from 'path'
import { fileURLToPath } from 'url'
const filename = fileURLToPath(import.meta.url)
const dirname = path.dirname(filename)
export default buildConfig({
admin: {
user: 'users',
importMap: {
baseDir: path.resolve(dirname),
},
},
collections: [Users, Media],
editor: lexicalEditor(),
secret: process.env.PAYLOAD_SECRET,
typescript: {
outputFile: path.resolve(dirname, 'payload-types.ts'),
},
db: mongooseAdapter({
url: process.env.DATABASE_URI,
}),
})
Essential Patterns
Basic Collection
import type { CollectionConfig } from 'payload'
export const Posts: CollectionConfig = {
slug: 'posts',
admin: {
useAsTitle: 'title',
defaultColumns: ['title', 'author', 'status', 'createdAt'],
},
fields: [
{ name: 'title', type: 'text', required: true },
{ name: 'slug', type: 'text', unique: true, index: true },
{ name: 'content', type: 'richText' },
{ name: 'author', type: 'relationship', relationTo: 'users' },
],
timestamps: true,
}
For more collection patterns (auth, upload, drafts, live preview), see COLLECTIONS.md.
Common Fields
// Text field
{ name: 'title', type: 'text', required: true }
// Relationship
{ name: 'author', type: 'relationship', relationTo: 'users', required: true }
// Rich text
{ name: 'content', type: 'richText', required: true }
// Select
{ name: 'status', type: 'select', options: ['draft', 'published'], defaultValue: 'draft' }
// Upload
{ name: 'image', type: 'upload', relationTo: 'media' }