Files
website-enchun-mgr/.agent/skills/payload-cms/references/fields.md
pkupuk ad8e2e313e chore(agent): configure AI agents and tools
Add configuration for BMad, Claude, OpenCode, and other AI agent tools and workflows.
2026-02-11 11:51:23 +08:00

5.6 KiB

Field Types Reference

Core Field Types

Text Fields

// Basic text
{ name: 'title', type: 'text', required: true }

// With validation
{
  name: 'email',
  type: 'text',
  validate: (value) => {
    if (!value?.includes('@')) return 'Invalid email'
    return true
  },
}

// With admin config
{
  name: 'description',
  type: 'textarea',
  admin: {
    placeholder: 'Enter description...',
    description: 'Brief summary',
  },
}

Slug Field Helper

Auto-generate URL-safe slugs:

import { slugField } from '@payloadcms/plugin-seo'

// Or manual implementation
{
  name: 'slug',
  type: 'text',
  unique: true,
  index: true,
  hooks: {
    beforeValidate: [
      ({ data, operation, originalDoc }) => {
        if (operation === 'create' || !originalDoc?.slug) {
          return data?.title?.toLowerCase().replace(/\s+/g, '-')
        }
        return originalDoc.slug
      },
    ],
  },
}

Number Fields

{ name: 'price', type: 'number', min: 0, required: true }
{ name: 'quantity', type: 'number', defaultValue: 1 }

Select Fields

// Simple select
{
  name: 'status',
  type: 'select',
  options: ['draft', 'published', 'archived'],
  defaultValue: 'draft',
}

// With labels
{
  name: 'priority',
  type: 'select',
  options: [
    { label: 'Low', value: 'low' },
    { label: 'Medium', value: 'medium' },
    { label: 'High', value: 'high' },
  ],
}

// Multi-select
{
  name: 'categories',
  type: 'select',
  hasMany: true,
  options: ['tech', 'design', 'marketing'],
}

Checkbox

{ name: 'featured', type: 'checkbox', defaultValue: false }

Date Fields

{ name: 'publishedAt', type: 'date' }

// With time
{
  name: 'eventDate',
  type: 'date',
  admin: { date: { pickerAppearance: 'dayAndTime' } },
}

Relationship Fields

Basic Relationship

// Single relationship
{
  name: 'author',
  type: 'relationship',
  relationTo: 'users',
  required: true,
}

// Multiple relationships (hasMany)
{
  name: 'tags',
  type: 'relationship',
  relationTo: 'tags',
  hasMany: true,
}

// Polymorphic (multiple collections)
{
  name: 'parent',
  type: 'relationship',
  relationTo: ['pages', 'posts'],
}

With Filter Options

Dynamically filter available options:

{
  name: 'relatedPosts',
  type: 'relationship',
  relationTo: 'posts',
  hasMany: true,
  filterOptions: ({ data }) => ({
    // Only show published posts, exclude self
    status: { equals: 'published' },
    id: { not_equals: data?.id },
  }),
}

Join Fields

Reverse relationship lookup (virtual field):

// In Posts collection
{
  name: 'comments',
  type: 'join',
  collection: 'comments',
  on: 'post', // field name in comments that references posts
}

Virtual Fields

Computed fields that don't store data:

{
  name: 'fullName',
  type: 'text',
  virtual: true,
  hooks: {
    afterRead: [
      ({ data }) => `${data?.firstName} ${data?.lastName}`,
    ],
  },
}

Conditional Fields

Show/hide fields based on other values:

{
  name: 'isExternal',
  type: 'checkbox',
},
{
  name: 'externalUrl',
  type: 'text',
  admin: {
    condition: (data) => data?.isExternal === true,
  },
}

Validation

Custom Validation

{
  name: 'slug',
  type: 'text',
  validate: (value, { data, operation }) => {
    if (!value) return 'Slug is required'
    if (!/^[a-z0-9-]+$/.test(value)) {
      return 'Slug must be lowercase letters, numbers, and hyphens only'
    }
    return true
  },
}

Async Validation

{
  name: 'username',
  type: 'text',
  validate: async (value, { payload }) => {
    if (!value) return true
    const existing = await payload.find({
      collection: 'users',
      where: { username: { equals: value } },
    })
    if (existing.docs.length > 0) return 'Username already taken'
    return true
  },
}

Group Fields

Organize related fields:

{
  name: 'meta',
  type: 'group',
  fields: [
    { name: 'title', type: 'text' },
    { name: 'description', type: 'textarea' },
  ],
}

Array Fields

Repeatable sets of fields:

{
  name: 'socialLinks',
  type: 'array',
  fields: [
    { name: 'platform', type: 'select', options: ['twitter', 'linkedin', 'github'] },
    { name: 'url', type: 'text' },
  ],
}

Blocks (Polymorphic Content)

Different content types in same array:

{
  name: 'layout',
  type: 'blocks',
  blocks: [
    {
      slug: 'hero',
      fields: [
        { name: 'heading', type: 'text' },
        { name: 'image', type: 'upload', relationTo: 'media' },
      ],
    },
    {
      slug: 'content',
      fields: [
        { name: 'richText', type: 'richText' },
      ],
    },
  ],
}

Point (Geolocation)

{
  name: 'location',
  type: 'point',
  label: 'Location',
}

// Query nearby
await payload.find({
  collection: 'stores',
  where: {
    location: {
      near: [-73.935242, 40.730610, 5000], // lng, lat, maxDistance (meters)
    },
  },
})

Upload Fields

{
  name: 'featuredImage',
  type: 'upload',
  relationTo: 'media',
  required: true,
}

Rich Text

{
  name: 'content',
  type: 'richText',
  // Lexical editor features configured in payload.config.ts
}

UI Fields (Presentational)

Fields that don't save data:

// Row layout
{
  type: 'row',
  fields: [
    { name: 'firstName', type: 'text', admin: { width: '50%' } },
    { name: 'lastName', type: 'text', admin: { width: '50%' } },
  ],
}

// Tabs
{
  type: 'tabs',
  tabs: [
    { label: 'Content', fields: [...] },
    { label: 'Meta', fields: [...] },
  ],
}

// Collapsible
{
  type: 'collapsible',
  label: 'Advanced Options',
  fields: [...],
}