Smithery Logo
MCPsSkillsDocsPricing
Login
Smithery Logo

Accelerating the Agent Economy

Resources

DocumentationPrivacy PolicySystem Status

Company

PricingAboutBlog

Connect

© 2026 Smithery. All rights reserved.

    payloadcms

    payload

    payloadcms/payload
    Coding
    40,462
    13 installs

    About

    SKILL.md

    Install

    Install via Skills CLI

    or add to your agent
    • Claude Code
      Claude Code
    • Codex
      Codex
    • OpenClaw
      OpenClaw
    • Cursor
      Cursor
    • Amp
      Amp
    • GitHub Copilot
      GitHub Copilot
    • Gemini CLI
      Gemini CLI
    • Kilo Code
      Kilo Code
    • Junie
      Junie
    • Replit
      Replit
    • Windsurf
      Windsurf
    • Cline
      Cline
    • Continue
      Continue
    • OpenCode
      OpenCode
    • OpenHands
      OpenHands
    • Roo Code
      Roo Code
    • Augment
      Augment
    • Goose
      Goose
    • Trae
      Trae
    • Zencoder
      Zencoder
    • Antigravity
      Antigravity
    ├─
    ├─
    └─

    About

    Use when working with Payload CMS projects (payload.config.ts, collections, fields, hooks, access control, Payload API).

    SKILL.md

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

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

    • 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
    Repository
    payloadcms/payload
    Files