title: Fields description: Field types, patterns, and configurations
// Auto-generate slugs
import { slugField } from 'payload'
slugField({ fieldToUse: 'title' })
// Relationship with filtering
{
name: 'category',
type: 'relationship',
relationTo: 'categories',
filterOptions: { active: { equals: true } },
}
// Conditional field
{
name: 'featuredImage',
type: 'upload',
relationTo: 'media',
admin: {
condition: (data) => data.featured === true,
},
}
// Virtual field
{
name: 'fullName',
type: 'text',
virtual: true,
hooks: {
afterRead: [({ siblingData }) => `${siblingData.firstName} ${siblingData.lastName}`],
},
}
{
name: 'title',
type: 'text',
required: true,
unique: true,
minLength: 5,
maxLength: 100,
index: true,
localized: true,
defaultValue: 'Default Title',
validate: (value) => Boolean(value) || 'Required',
admin: {
placeholder: 'Enter title...',
position: 'sidebar',
condition: (data) => data.showTitle === true,
},
}
import { lexicalEditor } from '@payloadcms/richtext-lexical'
import { HeadingFeature, LinkFeature } from '@payloadcms/richtext-lexical'
{
name: 'content',
type: 'richText',
required: true,
editor: lexicalEditor({
features: ({ defaultFeatures }) => [
...defaultFeatures,
HeadingFeature({
enabledHeadingSizes: ['h1', 'h2', 'h3'],
}),
LinkFeature({
enabledCollections: ['posts', 'pages'],
}),
],
}),
}
// Single relationship
{
name: 'author',
type: 'relationship',
relationTo: 'users',
required: true,
maxDepth: 2,
}
// Multiple relationships (hasMany)
{
name: 'categories',
type: 'relationship',
relationTo: 'categories',
hasMany: true,
filterOptions: {
active: { equals: true },
},
}
// Polymorphic relationship
{
name: 'relatedContent',
type: 'relationship',
relationTo: ['posts', 'pages'],
hasMany: true,
}
{
name: 'slides',
type: 'array',
minRows: 2,
maxRows: 10,
labels: {
singular: 'Slide',
plural: 'Slides',
},
fields: [
{
name: 'title',
type: 'text',
required: true,
},
{
name: 'image',
type: 'upload',
relationTo: 'media',
},
],
admin: {
initCollapsed: true,
},
}
import type { Block } from 'payload'
const HeroBlock: Block = {
slug: 'hero',
interfaceName: 'HeroBlock',
fields: [
{
name: 'heading',
type: 'text',
required: true,
},
{
name: 'background',
type: 'upload',
relationTo: 'media',
},
],
}
const ContentBlock: Block = {
slug: 'content',
fields: [
{
name: 'text',
type: 'richText',
},
],
}
{
name: 'layout',
type: 'blocks',
blocks: [HeroBlock, ContentBlock],
}
{
name: 'status',
type: 'select',
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Published', value: 'published' },
],
defaultValue: 'draft',
required: true,
}
// Multiple select
{
name: 'tags',
type: 'select',
hasMany: true,
options: ['tech', 'news', 'sports'],
}
{
name: 'featuredImage',
type: 'upload',
relationTo: 'media',
required: true,
filterOptions: {
mimeType: { contains: 'image' },
},
}
{
name: 'location',
type: 'point',
label: 'Location',
required: true,
}
// Query by distance
const nearbyLocations = await payload.find({
collection: 'stores',
where: {
location: {
near: [10, 20], // [longitude, latitude]
maxDistance: 5000, // in meters
minDistance: 1000,
},
},
})
// From Users collection - show user's orders
{
name: 'orders',
type: 'join',
collection: 'orders',
on: 'customer', // The field in 'orders' that references this user
}
// Tabs
{
type: 'tabs',
tabs: [
{
label: 'Content',
fields: [
{ name: 'title', type: 'text' },
{ name: 'body', type: 'richText' },
],
},
{
label: 'SEO',
fields: [
{ name: 'metaTitle', type: 'text' },
{ name: 'metaDescription', type: 'textarea' },
],
},
],
}
// Group (named)
{
name: 'meta',
type: 'group',
fields: [
{ name: 'title', type: 'text' },
{ name: 'description', type: 'textarea' },
],
}
{
name: 'email',
type: 'email',
validate: (value, { operation, data, siblingData }) => {
if (operation === 'create' && !value) {
return 'Email is required'
}
if (value && !value.includes('@')) {
return 'Invalid email format'
}
return true
},
}