Now liveThe Skillselion MCP - thousands of ranked skills, loaded into your agent mid-task. No install.Get it →
payloadcms avatar

Payload

  • 5.2k installs
  • 145 repo stars
  • Updated July 22, 2026
  • payloadcms/skills

payload is an agent skill for Payload CMS collections, fields, hooks, access control, queries, and plugin development in Next.js.

About

The payload skill guides Payload CMS development for Next.js native TypeScript projects with admin panel, REST and GraphQL APIs, authentication, and file storage. Quick reference maps tasks like slugField helpers, row-level access control, draft and publish versions, virtual computed fields, conditional admin fields, relationship filterOptions, hook loop prevention via req.context, cascading deletes, geospatial point queries, join reverse relationships, and Next.js revalidation in afterChange hooks. Setup uses npx create-payload-app with buildConfig, mongooseAdapter, lexicalEditor, and typed outputFile generation. Essential patterns cover CollectionConfig with timestamps, beforeChange slug hooks, type-safe Access functions, Local API find and findByID with depth control, AND OR query logic, and threading req through operations for transactions. Plugin development documents the options config wrapper pattern, preserving existing hooks, and adding fields via collection maps. Reference files split guidance across FIELDS, HOOKS, ACCESS-CONTROL, QUERIES, ADAPTERS, and PLUGIN-DEVELOPMENT docs for progressive lookup.

  • Maps common tasks to reference docs for fields, hooks, access, queries, and adapters.
  • Documents buildConfig with mongooseAdapter, lexicalEditor, and payload-types.ts output.
  • Covers type-safe Access, row-level where clauses, and Local API overrideAccess rules.
  • Explains hook loop prevention with req.context and Next.js revalidation patterns.
  • Includes plugin architecture for adding fields and hooks without breaking existing config.

Payload by the numbers

  • 5,191 all-time installs (skills.sh)
  • +157 installs in the week ending Aug 5, 2026 (Skillselion tracking)
  • Ranked #135 of 4,347 Backend & APIs skills by installs in the Skillselion catalog
  • Security screen: MEDIUM risk (skills.sh audit)
  • Data as of Aug 5, 2026 (Skillselion catalog sync)
At a glance

payload capabilities & compatibility

Capabilities
collection and field pattern lookup · access control and local api guidance · hook and transaction threading · plugin development patterns · draft publish and versioning setup
Use cases
api development · documentation · database
From the docs

What payload says it does

Payload is a Next.js native CMS with TypeScript-first architecture
SKILL.md
npx skills add https://github.com/payloadcms/skills --skill payload

Add your badge

Show developers this skill is listed on Skillselion. Paste this into your README.

Listed on Skillselion
Installs5.2k
repo stars145
Security audit2 / 3 scanners passed
Last updatedJuly 22, 2026
Repositorypayloadcms/skills

How do I configure Payload collections, fix validation errors, or implement access control and hooks correctly?

Develop Payload CMS projects with collections, fields, hooks, access control, Local API queries, adapters, and plugin patterns in Next.js TypeScript apps.

Who is it for?

Developers building or debugging Payload CMS projects with TypeScript and Next.js.

Skip if: Skip when the stack is not Payload CMS or the task is unrelated CMS admin customization.

When should I use this skill?

User works on payload.config.ts, collections, hooks, access control, or Payload Local API queries.

What you get

Working Payload config with grounded patterns for fields, hooks, queries, and plugins.

  • Collection configs
  • Access rules
  • Hook implementations

By the numbers

  • Reference tables link FIELDS HOOKS ACCESS-CONTROL QUERIES docs
  • create-payload-app scaffolds minimal buildConfig example

Files

SKILL.mdMarkdownGitHub ↗

Payload 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

TaskSolutionDetails
Auto-generate slugsslugField()FIELDS.md#slug-field-helper
Restrict content by userAccess control with queryACCESS-CONTROL.md#row-level-security-with-complex-queries
Local API user opsuser + overrideAccess: falseQUERIES.md#access-control-in-local-api
Draft/publish workflowversions: { drafts: true }COLLECTIONS.md#versioning--drafts
Computed fieldsvirtual: true with afterReadFIELDS.md#virtual-fields
Conditional fieldsadmin.conditionFIELDS.md#conditional-fields
Custom field validationvalidate functionFIELDS.md#text-field
Filter relationship listfilterOptions on fieldFIELDS.md#relationship
Select specific fieldsselect parameterQUERIES.md#local-api
Auto-set author/datesbeforeChange hookHOOKS.md#collection-hooks
Prevent hook loopsreq.context checkHOOKS.md#hook-context
Cascading deletesbeforeDelete hookHOOKS.md#collection-hooks
Geospatial queriespoint field with near/withinFIELDS.md#point-geolocation
Reverse relationshipsjoin field typeFIELDS.md#join-fields
Next.js revalidationContext control in afterChangeHOOKS.md#nextjs-revalidation-with-context-control
Query by relationshipNested property syntaxQUERIES.md#nested-properties
Complex queriesAND/OR logicQUERIES.md#andor-logic
TransactionsPass req to operationsADAPTERS.md#threading-req-through-operations
Background jobsJobs queue with tasksADVANCED.md#jobs-queue
Custom API routesCollection custom endpointsADVANCED.md#custom-endpoints
Cloud storageStorage adapter pluginsADAPTERS.md#storage-adapters
Multi-languagelocalization config + localized: trueADVANCED.md#localization
Create plugin(options) => (config) => ConfigPLUGIN-DEVELOPMENT.md#plugin-architecture
Plugin package setupPackage structure with SWCPLUGIN-DEVELOPMENT.md#plugin-package-structure
Add fields to collectionMap collections, spread fieldsPLUGIN-DEVELOPMENT.md#adding-fields-to-collections
Plugin hooksPreserve existing hooks in arrayPLUGIN-DEVELOPMENT.md#adding-hooks
Check field typeType guard functionsFIELD-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_URL,
  }),
})

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' }

For all field types (array, blocks, point, join, virtual, conditional, etc.), see FIELDS.md.

Hook Example

export const Posts: CollectionConfig = {
  slug: 'posts',
  hooks: {
    beforeChange: [
      async ({ data, operation }) => {
        if (operation === 'create') {
          data.slug = slugify(data.title)
        }
        return data
      },
    ],
  },
  fields: [{ name: 'title', type: 'text' }],
}

For all hook patterns, see HOOKS.md. For access control, see ACCESS-CONTROL.md.

Access Control with Type Safety

import type { Access } from 'payload'
import type { User } from '@/payload-types'

// Type-safe access control
export const adminOnly: Access = ({ req }) => {
  const user = req.user as User
  return user?.roles?.includes('admin') || false
}

// Row-level access control
export const ownPostsOnly: Access = ({ req }) => {
  const user = req.user as User
  if (!user) return false
  if (user.roles?.includes('admin')) return true

  return {
    author: { equals: user.id },
  }
}

Query Example

// Local API
const posts = await payload.find({
  collection: 'posts',
  where: {
    status: { equals: 'published' },
    'author.name': { contains: 'john' },
  },
  depth: 2,
  limit: 10,
  sort: '-createdAt',
})

// Query with populated relationships
const post = await payload.findByID({
  collection: 'posts',
  id: '123',
  depth: 2, // Populates relationships (default is 2)
})
// Returns: { author: { id: "user123", name: "John" } }

// Without depth, relationships return IDs only
const post = await payload.findByID({
  collection: 'posts',
  id: '123',
  depth: 0,
})
// Returns: { author: "user123" }

For all query operators and REST/GraphQL examples, see QUERIES.md.

Getting Payload Instance

// In API routes (Next.js)
import { getPayload } from 'payload'
import config from '@payload-config'

export async function GET() {
  const payload = await getPayload({ config })

  const posts = await payload.find({
    collection: 'posts',
  })

  return Response.json(posts)
}

// In Server Components
import { getPayload } from 'payload'
import config from '@payload-config'

export default async function Page() {
  const payload = await getPayload({ config })
  const { docs } = await payload.find({ collection: 'posts' })

  return <div>{docs.map(post => <h1 key={post.id}>{post.title}</h1>)}</div>
}

Logger Usage

// ✅ Valid: single string
payload.logger.error('Something went wrong')

// ✅ Valid: object with msg and err
payload.logger.error({ msg: 'Failed to process', err: error })

// ❌ Invalid: don't pass error as second argument
payload.logger.error('Failed to process', error)

// ❌ Invalid: use `err` not `error`, use `msg` not `message`
payload.logger.error({ message: 'Failed', error: error })

Security Pitfalls

1. Local API Access Control (CRITICAL)

By default, Local API operations bypass ALL access control, even when passing a user.

// ❌ SECURITY BUG: Passes user but ignores their permissions
await payload.find({
  collection: 'posts',
  user: someUser, // Access control is BYPASSED!
})

// ✅ SECURE: Actually enforces the user's permissions
await payload.find({
  collection: 'posts',
  user: someUser,
  overrideAccess: false, // REQUIRED for access control
})

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.

// ❌ DATA CORRUPTION RISK: Separate transaction
hooks: {
  afterChange: [
    async ({ doc, req }) => {
      await req.payload.create({
        collection: 'audit-log',
        data: { docId: doc.id },
        // Missing req - runs in separate transaction!
      })
    },
  ]
}

// ✅ ATOMIC: Same transaction
hooks: {
  afterChange: [
    async ({ doc, req }) => {
      await req.payload.create({
        collection: 'audit-log',
        data: { docId: doc.id },
        req, // Maintains atomicity
      })
    },
  ]
}

See ADAPTERS.md#threading-req-through-operations.

3. Infinite Hook Loops

Hooks triggering operations that trigger the same hooks create infinite loops.

// ❌ INFINITE LOOP
hooks: {
  afterChange: [
    async ({ doc, req }) => {
      await req.payload.update({
        collection: 'posts',
        id: doc.id,
        data: { views: doc.views + 1 },
        req,
      }) // Triggers afterChange again!
    },
  ]
}

// ✅ SAFE: Use context flag
hooks: {
  afterChange: [
    async ({ doc, req, context }) => {
      if (context.skipHooks) return

      await req.payload.update({
        collection: 'posts',
        id: doc.id,
        data: { views: doc.views + 1 },
        context: { skipHooks: true },
        req,
      })
    },
  ]
}

See HOOKS.md#context.

Project Structure

src/
├── app/
│   ├── (frontend)/
│   │   └── page.tsx
│   └── (payload)/
│       └── admin/[[...segments]]/page.tsx
├── collections/
│   ├── Posts.ts
│   ├── Media.ts
│   └── Users.ts
├── globals/
│   └── Header.ts
├── components/
│   └── CustomField.tsx
├── hooks/
│   └── slugify.ts
└── payload.config.ts

Type Generation

// payload.config.ts
export default buildConfig({
  typescript: {
    outputFile: path.resolve(dirname, 'payload-types.ts'),
  },
  // ...
})

// Usage
import type { Post, User } from '@/payload-types'

Reference Documentation

  • [FIELDS.md](reference/FIELDS.md) - All field types, validation, admin options
  • [FIELD-TYPE-GUARDS.md](reference/FIELD-TYPE-GUARDS.md) - Type guards for runtime field type checking and narrowing
  • [COLLECTIONS.md](reference/COLLECTIONS.md) - Collection configs, auth, upload, drafts, live preview
  • [HOOKS.md](reference/HOOKS.md) - Collection hooks, field hooks, context patterns
  • [ACCESS-CONTROL.md](reference/ACCESS-CONTROL.md) - Collection, field, global access control, RBAC, multi-tenant
  • [ACCESS-CONTROL-ADVANCED.md](reference/ACCESS-CONTROL-ADVANCED.md) - Context-aware, time-based, subscription-based access, factory functions, templates
  • [QUERIES.md](reference/QUERIES.md) - Query operators, Local/REST/GraphQL APIs
  • [ENDPOINTS.md](reference/ENDPOINTS.md) - Custom API endpoints: authentication, helpers, request/response patterns
  • [ADAPTERS.md](reference/ADAPTERS.md) - Database, storage, email adapters, transactions
  • [ADVANCED.md](reference/ADVANCED.md) - Authentication, jobs, endpoints, components, plugins, localization
  • [PLUGIN-DEVELOPMENT.md](reference/PLUGIN-DEVELOPMENT.md) - Plugin architecture, monorepo structure, patterns, best practices

Resources

  • llms-full.txt: <https://payloadcms.com/llms-full.txt>
  • Docs: <https://payloadcms.com/docs>
  • GitHub: <https://github.com/payloadcms/payload>
  • Examples: <https://github.com/payloadcms/payload/tree/main/examples>
  • Templates: <https://github.com/payloadcms/payload/tree/main/templates>

Related skills

How it compares

Choose payload skill guidance when your stack is Payload CMS and you need in-context patterns for hooks and access control instead of generic headless CMS advice.

FAQ

Who is payload for?

Developers building Payload CMS apps who need grounded patterns for collections, hooks, and access control.

When should I use it?

When debugging Payload validation, relationships, transactions, hooks, or plugin development.

Is payload safe to install?

Review the Security Audits panel; access control and secrets handling require production review.

Backend & APIsbackenddocs

This week in AI coding

Five minutes, every Monday - the tools, releases and tactics for developers.

unsubscribe anytime.