Payload
Build and debug Payload CMS projects with TypeScript-first architecture
✨ The solution you've been looking for
Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API). Use when debugging validation errors, security issues, relationship queries, transactions, or hook behavior.
See It In Action
Interactive preview & real-world examples
AI Conversation Simulator
See how users interact with this skill
User Prompt
Help me create a blog collection with posts, authors, and categories. Posts should have drafts, rich text content, and SEO fields.
Skill Processing
Analyzing request...
Agent Response
Complete collection configurations with proper TypeScript types, field definitions, and relationship setup
Quick Start (3 Steps)
Get up and running in minutes
Install
claude-code skill install payload
claude-code skill install payloadConfig
First Trigger
@payload helpCommands
| Command | Description | Required Args |
|---|---|---|
| @payload content-model-design | Define collections, fields, and relationships for your CMS structure | None |
| @payload access-control-implementation | Set up role-based permissions and row-level security for content | None |
| @payload hook-and-validation-logic | Implement business logic with beforeChange, afterChange, and validation hooks | None |
Typical Use Cases
Content Model Design
Define collections, fields, and relationships for your CMS structure
Access Control Implementation
Set up role-based permissions and row-level security for content
Hook and Validation Logic
Implement business logic with beforeChange, afterChange, and validation hooks
Overview
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
1npx create-payload-app@latest my-app
2cd my-app
3pnpm dev
Minimal Config
1import { buildConfig } from 'payload'
2import { mongooseAdapter } from '@payloadcms/db-mongodb'
3import { lexicalEditor } from '@payloadcms/richtext-lexical'
4import path from 'path'
5import { fileURLToPath } from 'url'
6
7const filename = fileURLToPath(import.meta.url)
8const dirname = path.dirname(filename)
9
10export default buildConfig({
11 admin: {
12 user: 'users',
13 importMap: {
14 baseDir: path.resolve(dirname),
15 },
16 },
17 collections: [Users, Media],
18 editor: lexicalEditor(),
19 secret: process.env.PAYLOAD_SECRET,
20 typescript: {
21 outputFile: path.resolve(dirname, 'payload-types.ts'),
22 },
23 db: mongooseAdapter({
24 url: process.env.DATABASE_URL,
25 }),
26})
Essential Patterns
Basic Collection
1import type { CollectionConfig } from 'payload'
2
3export const Posts: CollectionConfig = {
4 slug: 'posts',
5 admin: {
6 useAsTitle: 'title',
7 defaultColumns: ['title', 'author', 'status', 'createdAt'],
8 },
9 fields: [
10 { name: 'title', type: 'text', required: true },
11 { name: 'slug', type: 'text', unique: true, index: true },
12 { name: 'content', type: 'richText' },
13 { name: 'author', type: 'relationship', relationTo: 'users' },
14 ],
15 timestamps: true,
16}
For more collection patterns (auth, upload, drafts, live preview), see COLLECTIONS.md.
Common Fields
1// Text field
2{ name: 'title', type: 'text', required: true }
3
4// Relationship
5{ name: 'author', type: 'relationship', relationTo: 'users', required: true }
6
7// Rich text
8{ name: 'content', type: 'richText', required: true }
9
10// Select
11{ name: 'status', type: 'select', options: ['draft', 'published'], defaultValue: 'draft' }
12
13// Upload
14{ name: 'image', type: 'upload', relationTo: 'media' }
For all field types (array, blocks, point, join, virtual, conditional, etc.), see FIELDS.md.
Hook Example
1export const Posts: CollectionConfig = {
2 slug: 'posts',
3 hooks: {
4 beforeChange: [
5 async ({ data, operation }) => {
6 if (operation === 'create') {
7 data.slug = slugify(data.title)
8 }
9 return data
10 },
11 ],
12 },
13 fields: [{ name: 'title', type: 'text' }],
14}
For all hook patterns, see HOOKS.md. For access control, see ACCESS-CONTROL.md.
Access Control with Type Safety
1import type { Access } from 'payload'
2import type { User } from '@/payload-types'
3
4// Type-safe access control
5export const adminOnly: Access = ({ req }) => {
6 const user = req.user as User
7 return user?.roles?.includes('admin') || false
8}
9
10// Row-level access control
11export const ownPostsOnly: Access = ({ req }) => {
12 const user = req.user as User
13 if (!user) return false
14 if (user.roles?.includes('admin')) return true
15
16 return {
17 author: { equals: user.id },
18 }
19}
Query Example
1// Local API
2const posts = await payload.find({
3 collection: 'posts',
4 where: {
5 status: { equals: 'published' },
6 'author.name': { contains: 'john' },
7 },
8 depth: 2,
9 limit: 10,
10 sort: '-createdAt',
11})
12
13// Query with populated relationships
14const post = await payload.findByID({
15 collection: 'posts',
16 id: '123',
17 depth: 2, // Populates relationships (default is 2)
18})
19// Returns: { author: { id: "user123", name: "John" } }
20
21// Without depth, relationships return IDs only
22const post = await payload.findByID({
23 collection: 'posts',
24 id: '123',
25 depth: 0,
26})
27// Returns: { author: "user123" }
For all query operators and REST/GraphQL examples, see QUERIES.md.
Getting Payload Instance
1// In API routes (Next.js)
2import { getPayload } from 'payload'
3import config from '@payload-config'
4
5export async function GET() {
6 const payload = await getPayload({ config })
7
8 const posts = await payload.find({
9 collection: 'posts',
10 })
11
12 return Response.json(posts)
13}
14
15// In Server Components
16import { getPayload } from 'payload'
17import config from '@payload-config'
18
19export default async function Page() {
20 const payload = await getPayload({ config })
21 const { docs } = await payload.find({ collection: 'posts' })
22
23 return <div>{docs.map(post => <h1 key={post.id}>{post.title}</h1>)}</div>
24}
Security Pitfalls
1. Local API Access Control (CRITICAL)
By default, Local API operations bypass ALL access control, even when passing a user.
1// ❌ SECURITY BUG: Passes user but ignores their permissions
2await payload.find({
3 collection: 'posts',
4 user: someUser, // Access control is BYPASSED!
5})
6
7// ✅ SECURE: Actually enforces the user's permissions
8await payload.find({
9 collection: 'posts',
10 user: someUser,
11 overrideAccess: false, // REQUIRED for access control
12})
When to use each:
overrideAccess: true(default) - Server-side operations you trust (cron jobs, system tasks)overrideAccess: false- When operating on behalf of a user (API routes, webhooks)
See QUERIES.md#access-control-in-local-api.
2. Transaction Failures in Hooks
Nested operations in hooks without req break transaction atomicity.
1// ❌ DATA CORRUPTION RISK: Separate transaction
2hooks: {
3 afterChange: [
4 async ({ doc, req }) => {
5 await req.payload.create({
6 collection: 'audit-log',
7 data: { docId: doc.id },
8 // Missing req - runs in separate transaction!
9 })
10 },
11 ]
12}
13
14// ✅ ATOMIC: Same transaction
15hooks: {
16 afterChange: [
17 async ({ doc, req }) => {
18 await req.payload.create({
19 collection: 'audit-log',
20 data: { docId: doc.id },
21 req, // Maintains atomicity
22 })
23 },
24 ]
25}
See ADAPTERS.md#threading-req-through-operations.
3. Infinite Hook Loops
Hooks triggering operations that trigger the same hooks create infinite loops.
1// ❌ INFINITE LOOP
2hooks: {
3 afterChange: [
4 async ({ doc, req }) => {
5 await req.payload.update({
6 collection: 'posts',
7 id: doc.id,
8 data: { views: doc.views + 1 },
9 req,
10 }) // Triggers afterChange again!
11 },
12 ]
13}
14
15// ✅ SAFE: Use context flag
16hooks: {
17 afterChange: [
18 async ({ doc, req, context }) => {
19 if (context.skipHooks) return
20
21 await req.payload.update({
22 collection: 'posts',
23 id: doc.id,
24 data: { views: doc.views + 1 },
25 context: { skipHooks: true },
26 req,
27 })
28 },
29 ]
30}
See HOOKS.md#context.
Project Structure
1src/
2├── app/
3│ ├── (frontend)/
4│ │ └── page.tsx
5│ └── (payload)/
6│ └── admin/[[...segments]]/page.tsx
7├── collections/
8│ ├── Posts.ts
9│ ├── Media.ts
10│ └── Users.ts
11├── globals/
12│ └── Header.ts
13├── components/
14│ └── CustomField.tsx
15├── hooks/
16│ └── slugify.ts
17└── payload.config.ts
Type Generation
1// payload.config.ts
2export default buildConfig({
3 typescript: {
4 outputFile: path.resolve(dirname, 'payload-types.ts'),
5 },
6 // ...
7})
8
9// Usage
10import type { Post, User } from '@/payload-types'
Reference Documentation
- FIELDS.md - All field types, validation, admin options
- FIELD-TYPE-GUARDS.md - Type guards for runtime field type checking and narrowing
- COLLECTIONS.md - Collection configs, auth, upload, drafts, live preview
- HOOKS.md - Collection hooks, field hooks, context patterns
- ACCESS-CONTROL.md - Collection, field, global access control, RBAC, multi-tenant
- ACCESS-CONTROL-ADVANCED.md - Context-aware, time-based, subscription-based access, factory functions, templates
- QUERIES.md - Query operators, Local/REST/GraphQL APIs
- ENDPOINTS.md - Custom API endpoints: authentication, helpers, request/response patterns
- ADAPTERS.md - Database, storage, email adapters, transactions
- ADVANCED.md - Authentication, jobs, endpoints, components, plugins, localization
- PLUGIN-DEVELOPMENT.md - Plugin architecture, monorepo structure, patterns, best practices
Resources
What Users Are Saying
Real feedback from the community
Environment Matrix
Dependencies
Framework Support
Context Window
Security & Privacy
Information
- Author
- payloadcms
- Updated
- 2026-01-30
- Category
- productivity-tools
Related Skills
Payload
Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access …
View Details →N8N Workflow Patterns
Proven workflow architectural patterns from real n8n workflows. Use when building new workflows, …
View Details →N8N Workflow Patterns
Proven workflow architectural patterns from real n8n workflows. Use when building new workflows, …
View Details →