Back to Payload

Fields Overview

docs/fields/overview.mdx

3.84.149.8 KB
Original Source

Fields are the building blocks of Payload. They define the schema of the Documents that will be stored in the Database, as well as automatically generate the corresponding UI within the Admin Panel.

There are many Field Types to choose from, ranging anywhere from simple text strings to nested arrays and blocks. Most fields save data to the database, while others are strictly presentational. Fields can have Custom Validations, Conditional Logic, Access Control, Hooks, and so much more.

Fields can be endlessly customized in their appearance and behavior without affecting their underlying data structure. Fields are designed to withstand heavy modification or even complete replacement through the use of Custom Field Components.

To configure fields, use the fields property in your Collection or Global config:

ts
import type { CollectionConfig } from 'payload'

export const Page: CollectionConfig = {
  // ...
  fields: [
    // highlight-line
    // ...
  ],
}

Field Types

Payload provides a wide variety of built-in Field Types, each with its own unique properties and behaviors that determine which values it can accept, how it is presented in the API, and how it will be rendered in the Admin Panel.

To configure fields, use the fields property in your Collection or Global config:

ts
import type { CollectionConfig } from 'payload'

export const Page: CollectionConfig = {
  slug: 'pages',
  // highlight-start
  fields: [
    {
      name: 'field',
      type: 'text',
    },
  ],
  // highlight-end
}
<Banner type="warning"> **Reminder:** Each field is an object with at least the `type` property. This matches the field to its corresponding Field Type. [More details](#field-options). </Banner>

There are three main categories of fields in Payload:

To begin writing fields, first determine which Field Type best supports your application. Then author your field accordingly using the Field Options for your chosen field type.

Data Fields

Data Fields are used to store data in the Database. All Data Fields have a name property. This is the key that will be used to store the field's value.

Here are the available Data Fields:

  • Array - for repeating content, supports nested fields
  • Blocks - for block-based content, supports nested fields
  • Checkbox - saves boolean true / false values
  • Code - renders a code editor interface that saves a string
  • Date - renders a date picker and saves a timestamp
  • Email - ensures the value is a properly formatted email address
  • Group - nests fields within a keyed object
  • JSON - renders a JSON editor interface that saves a JSON object
  • Number - saves numeric values
  • Point - for location data, saves geometric coordinates
  • Radio - renders a radio button group that allows only one value to be selected
  • Relationship - assign relationships to other collections
  • Rich Text - renders a fully extensible rich text editor
  • Select - renders a dropdown / picklist style value selector
  • Tabs (Named) - similar to group, but renders nested fields within a tabbed layout
  • Text - simple text input that saves a string
  • Textarea - similar to text, but allows for multi-line input
  • Upload - allows local file and image upload

Presentational Fields

Presentational Fields do not store data in the database. Instead, they are used to organize and present other fields in the Admin Panel, or to add custom UI components.

Here are the available Presentational Fields:

  • Collapsible - nests fields within a collapsible component
  • Row - aligns fields horizontally
  • Tabs (Unnamed) - nests fields within a tabbed layout. It is not presentational if the tab has a name.
  • Group (Unnamed) - nests fields within a keyed object. It is not presentational if the group has a name.
  • UI - blank field for custom UI components

Virtual Fields

Virtual fields display data that is not stored in the database, but is computed or derived from other fields.

Here are the available Virtual Fields:

  • Join - achieves two-way data binding between fields
<Banner type="success"> **Tip:** Don't see a built-in field type that you need? Build it! Using a combination of [Field Validations](#validation) and [Custom Components](../custom-components/overview), you can override the entirety of how a component functions within the [Admin Panel](../admin/overview) to effectively create your own field type. </Banner>

Virtual Field Configuration

While Join fields are purpose-built virtual field types, any field type can be made virtual by adding the virtual property to its configuration. This allows you to create computed or relationship-derived fields that appear in API responses without being stored in the database.

Virtual fields are populated during API responses and can be used in the Admin Panel, but their values are not persisted to the database. This makes them ideal for displaying read-only computed data, relationship summaries, or formatted versions of existing field data.

Configuring Virtual Fields

Any field type can be made virtual by adding the virtual property to the field configuration. The virtual property can be configured in two ways:

Boolean Virtual Fields

When virtual is set to true, the field becomes virtual but doesn't automatically populate any data. You'll typically use Field-level Hooks to compute and populate the field's value:

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

String Path Virtual Fields

When virtual is set to a string path, it creates a "virtual relationship field" that automatically resolves to data from another field in the document. This is particularly useful for displaying relationship data:

ts
{
  name: 'authorName',
  type: 'text',
  virtual: 'author.name' // Resolves to the 'name' field of the 'author' relationship
}

Virtual Path Syntax

Virtual paths use dot notation to traverse relationships and nested data:

  • author.name - Gets the name field from the author relationship
  • author.profile.bio - Gets the bio field from a nested profile object within the author relationship
  • categories.title - For hasMany relationships, returns an array of title values
  • request.additionalStakeholders.email - Traverses multiple relationship levels

Important Requirements for Virtual Path Fields:

  1. Source Relationship Required: The document must have a relationship field that corresponds to the first part of the virtual path. For example, if using virtual: 'author.name', there must be an author relationship field defined in the same collection.

  2. Path Resolution: Virtual paths resolve at query time by following the relationships and extracting the specified field values.

  3. Array Handling: When the virtual path traverses a hasMany relationship, the result will be an array of values.

Common Use Cases

Displaying Relationship Names

Instead of just showing relationship IDs, display the actual names or titles:

ts
// Original relationship field
{
  name: 'author',
  type: 'relationship',
  relationTo: 'users'
},
// Virtual field to display author's name
{
  name: 'authorName',
  type: 'text',
  virtual: 'author.name'
}

Multiple Relationship Values

For hasMany relationships, virtual fields return arrays:

ts
// Original relationship field
{
  name: 'categories',
  type: 'relationship',
  relationTo: 'categories',
  hasMany: true
},
// Virtual field to display category titles
{
  name: 'categoryTitles',
  type: 'text',
  virtual: 'categories.title' // Returns ['Tech', 'News', 'Updates']
}

Computed Values

Use hooks to create computed virtual fields:

ts
{
  name: 'wordCount',
  type: 'number',
  virtual: true,
  hooks: {
    afterRead: [
      ({ siblingData }) => {
        const content = siblingData.content || ''
        return content.split(/\s+/).length
      }
    ]
  }
}

Virtual Fields in API Responses

Virtual fields appear in API responses alongside regular fields:

json
{
  "id": "123",
  "title": "My Post",
  "author": "64f1234567890abcdef12345",
  "authorName": "John Doe", // Virtual field
  "categories": ["64f9876543210fedcba67890", "64f5432109876543210abcdef"],
  "categoryTitles": ["Tech", "News"], // Virtual field
  "wordCount": 450 // Virtual field
}
<Banner type="warning"> **Important:** When using virtual path fields, ensure that the referenced relationship field exists in your schema. Virtual paths like `author.name` require an `author` relationship field to be defined, otherwise the virtual field will not resolve properly. </Banner>

Field Options

All fields require at least the type property. This matches the field to its corresponding Field Type to determine its appearance and behavior within the Admin Panel. Each Field Type has its own unique set of options based on its own type.

To set a field's type, use the type property in your Field Config:

ts
import type { Field } from 'payload'

export const MyField: Field = {
  type: 'text', // highlight-line
  name: 'myField',
}
<Banner type="warning"> For a full list of configuration options, see the documentation for each [Field Type](#field-types). </Banner>

Field Names

All Data Fields require a name property. This is the key that will be used to store and retrieve the field's value in the database. This property must be unique amongst this field's siblings.

To set a field's name, use the name property in your Field Config:

ts
import type { Field } from 'payload'

export const MyField: Field = {
  type: 'text',
  name: 'myField', // highlight-line
}

Since field names map directly to keys in API response objects and to column names in the database, they should follow JavaScript identifier conventions: start with a letter or underscore, and contain only letters, numbers, and underscores. Just like all Payload field names are strings, all Payload response objects use strings as their keys — so treat field names as you would a JavaScript variable name.

<Banner type="warning"> Avoid field names that start with a digit (e.g. `'1'`, `'2field'`) or contain hyphens (e.g. `'field-name'`). While these are valid strings in TypeScript, they can cause unexpected behavior when Payload accesses data using dot-notation paths, generates GraphQL schemas, or maps fields to database columns. GraphQL identifiers do not allow hyphens, so hyphenated field names will cause schema generation to fail. </Banner>

Payload reserves various field names for internal use. Using reserved field names will result in your field being sanitized from the config.

The following field names are forbidden and cannot be used:

  • __v
  • salt
  • hash
  • file
  • status - with Postgres Adapter and when drafts are enabled

Field-level Hooks

In addition to being able to define Hooks on a document-level, you can define extremely granular logic field-by-field.

To define Field-level Hooks, use the hooks property in your Field Config:

ts
import type { Field } from 'payload'

export const MyField: Field = {
  type: 'text',
  name: 'myField',
  // highlight-start
  hooks: {
    // ...
  },
  // highlight-end
}

For full details on Field-level Hooks, see the Field Hooks documentation.

Field-level Access Control

In addition to being able to define Access Control on a document-level, you can define extremely granular permissions field-by-field.

To define Field-level Access Control, use the access property in your Field Config:

ts
import type { Field } from 'payload'

export const MyField: Field = {
  type: 'text',
  name: 'myField',
  // highlight-start
  access: {
    // ...
  },
  // highlight-end
}

For full details on Field-level Access Control, see the Field Access Control documentation.

Default Values

Fields can be optionally prefilled with initial values. This is used in both the Admin Panel as well as API requests to populate missing or undefined field values during the create or update operations.

To set a field's default value, use the defaultValue property in your Field Config:

ts
import type { Field } from 'payload'

export const MyField: Field = {
  type: 'text',
  name: 'myField',
  defaultValue: 'Hello, World!', // highlight-line
}

Default values can be defined as a static value or a function that returns a value. When a defaultValue is defined statically, Payload's Database Adapters will apply it to the database schema or models.

Functions can be written to make use of the following argument properties:

  • user - the authenticated user object
  • locale - the currently selected locale string
  • req - the PayloadRequest object

Here is an example of a defaultValue function:

ts
import type { Field } from 'payload'

const translation: {
  en: 'Written by'
  es: 'Escrito por'
}

export const myField: Field = {
  name: 'attribution',
  type: 'text',
  // highlight-start
  defaultValue: ({ user, locale, req }) =>
    `${translation[locale]} ${user.name}`,
  // highlight-end
}
<Banner type="success"> **Tip:** You can use async `defaultValue` functions to fill fields with data from API requests or Local API using `req.payload`. </Banner>

Validation

Fields are automatically validated based on their Field Type and other Field Options such as required or min and max value constraints. If needed, however, field validations can be customized or entirely replaced by providing your own custom validation functions.

To set a custom field validation function, use the validate property in your Field Config:

ts
import type { Field } from 'payload'

export const MyField: Field = {
  type: 'text',
  name: 'myField',
  validate: (value) => Boolean(value) || 'This field is required', // highlight-line
}

Custom validation functions should return either true or a string representing the error message to display in API responses.

The following arguments are provided to the validate function:

ArgumentDescription
valueThe value of the field being validated.
ctxAn object with additional data and context. More details

Validation Context

The ctx argument contains full document data, sibling field data, the current operation, and other useful information such as currently authenticated user:

ts
import type { Field } from 'payload'

export const MyField: Field = {
  type: 'text',
  name: 'myField',
  // highlight-start
  validate: (val, { user }) =>
    Boolean(user) || 'You must be logged in to save this field',
  // highlight-end
}

The following additional properties are provided in the ctx object:

PropertyDescription
dataAn object containing the full collection or global document currently being edited.
siblingDataAn object containing document data that is scoped to only fields within the same parent of this field.
operationWill be create or update depending on the UI action or API call.
pathThe full path to the field in the schema, represented as an array of string segments, including array indexes. I.e ['group', 'myArray', '1', 'textField'].
idThe id of the current document being edited. id is undefined during the create operation.
reqThe current HTTP request object. Contains payload, user, etc.
eventEither onChange or submit depending on the current action. Used as a performance opt-in. More details.

Localized and Built-in Error Messages

You can return localized error messages by utilizing the translation function provided in the req object:

ts
import type { Field } from 'payload'

export const MyField: Field = {
  type: 'text',
  name: 'myField',
  validate: (value, { req: { t } }) =>
    Boolean(value) || t('validation:required'), // highlight-line
}

This way you can use Custom Translations as well as Payload's built in error messages (like validation:required used in the example above). For a full list of available translation strings, see the english translation file of Payload.

Reusing Default Field Validations

When using custom validation functions, Payload will use yours in place of the default. However, you might want to simply augment the default validation with your own custom logic.

To reuse default field validations, call them from within your custom validation function:

ts
import { text } from 'payload/shared'

const field: Field = {
  name: 'notBad',
  type: 'text',
  validate: (val, args) => {
    if (val === 'bad') return 'This cannot be "bad"'
    return text(val, args) // highlight-line
  },
}

Here is a list of all default field validation functions:

ts
import {
  array,
  blocks,
  checkbox,
  code,
  date,
  email,
  json,
  number,
  point,
  radio,
  relationship,
  richText,
  select,
  tabs,
  text,
  textarea,
  upload,
} from 'payload/shared'

Validation Performance

When writing async or computationally heavy validation functions, it is important to consider the performance implications. Within the Admin Panel, validations are executed on every change to the field, so they should be as lightweight as possible and only run when necessary.

If you need to perform expensive validations, such as querying the database, consider using the event property in the ctx object to only run that particular validation on form submission.

To write asynchronous validation functions, use the async keyword to define your function:

ts
import type { CollectionConfig } from 'payload'

export const Orders: CollectionConfig = {
  slug: 'orders',
  fields: [
    {
      name: 'customerNumber',
      type: 'text',
      // highlight-start
      validate: async (val, { event }) => {
        if (event === 'onChange') {
          return true
        }

        // only perform expensive validation when the form is submitted
        const response = await fetch(`https://your-api.com/customers/${val}`)

        if (response.ok) {
          return true
        }

        return 'The customer number provided does not match any customers within our records.'
      },
      // highlight-end
    },
  ],
}
<Banner type="success"> For more performance tips, see the [Performance documentation](../performance/overview). </Banner>

Custom ID Fields

All Collections automatically generate their own ID field. If needed, you can override this behavior by providing an explicit ID field to your config. This field should either be required or have a hook to generate the ID dynamically.

To define a custom ID field, add a top-level field with the name property set to id:

ts
import type { CollectionConfig } from 'payload'

export const MyCollection: CollectionConfig = {
  // ...
  fields: [
    {
      name: 'id', // highlight-line
      required: true,
      type: 'number',
    },
  ],
}
<Banner type="warning"> **Reminder:** The Custom ID Fields can only be of type [`Number`](./number) or [`Text`](./text). Custom ID fields with type `text` must not contain `/` or `.` characters. </Banner>

Admin Options

You can customize the appearance and behavior of fields within the Admin Panel through the admin property of any Field Config:

ts
import type { CollectionConfig } from 'payload'

export const CollectionConfig: CollectionConfig = {
  // ...
  fields: [
    // ...
    {
      name: 'myField',
      type: 'text',
      admin: {
        // highlight-line
        // ...
      },
    },
  ],
}

The following options are available:

OptionDescription
conditionProgrammatically show / hide fields based on other fields. More details.
componentsAll Field Components can be swapped out for Custom Components that you define.
descriptionHelper text to display alongside the field to provide more information for the editor. More details.
positionUse 'sidebar' to render the field in the sidebar or 'main' (default) to keep it in the main area.
widthRestrict the width of a field. You can pass any string-based value here, be it pixels, percentages, etc. This property is especially useful when fields are nested within a Row type where they can be organized horizontally.
styleCSS Properties to inject into the root element of the field.
classNameAttach a CSS class attribute to the root DOM element of a field.
readOnlySetting a field to readOnly has no effect on the API whatsoever but disables the admin component's editability to prevent editors from modifying the field's value.
disabledIf a field is disabled, it is completely omitted from the Admin Panel entirely.
disableBulkEditSet disableBulkEdit to true to prevent fields from appearing in the select options when making edits for multiple documents. Defaults to true for UI fields.
disableGroupBySet disableGroupBy to true to prevent fields from appearing in the list view groupBy options. Defaults to false.
disableListColumnSet disableListColumn to true to prevent fields from appearing in the list view column selector. Defaults to false.
disableListFilterSet disableListFilter to true to prevent fields from appearing in the list view filter options. Defaults to false.
hiddenWill transform the field into a hidden input type. Its value will still submit with requests in the Admin Panel, but the field itself will not be visible to editors.

Field Descriptions

Field Descriptions are used to provide additional information to the editor about a field, such as special instructions. Their placement varies from field to field, but typically are displayed with subtle style differences beneath the field inputs.

A description can be configured in three ways:

To add a Custom Description to a field, use the admin.description property in your Field Config:

ts
import type { CollectionConfig } from 'payload'

export const MyCollectionConfig: CollectionConfig = {
  // ...
  fields: [
    // ...
    {
      name: 'myField',
      type: 'text',
      admin: {
        description: 'Hello, world!', // highlight-line
      },
    },
  ],
}
<Banner type="warning"> **Reminder:** To replace the Field Description with a [Custom Component](../custom-components/overview), use the `admin.components.Description` property. [More details](#description). </Banner>

Description Functions

Custom Descriptions can also be defined as a function. Description Functions are executed on the server and can be used to format simple descriptions based on the user's current Locale.

To add a Description Function to a field, set the admin.description property to a function in your Field Config:

ts
import type { CollectionConfig } from 'payload'

export const MyCollectionConfig: CollectionConfig = {
  // ...
  fields: [
    // ...
    {
      name: 'myField',
      type: 'text',
      admin: {
        description: ({ t }) => `${t('Hello, world!')}`, // highlight-line
      },
    },
  ],
}

All Description Functions receive the following arguments:

ArgumentDescription
tThe t function used to internationalize the Admin Panel. More details
<Banner type="info"> **Note:** If you need to subscribe to live updates within your form, use a Description Component instead. [More details](#description). </Banner>

Conditional Logic

You can show and hide fields based on what other fields are doing by utilizing conditional logic on a field by field basis. The condition property on a field's admin config accepts a function which takes the following arguments:

ArgumentDescription
dataThe entire document's data that is currently being edited.
siblingDataOnly the fields that are direct siblings to the field with the condition.
ctxAn object containing additional information about the field’s location and user.

The ctx object:

PropertyDescription
blockDataThe nearest parent block's data. If the field is not inside a block, this will be undefined.
operationA string relating to which operation the field type is currently executing within.
pathThe full path to the field in the schema, represented as an array of string segments, including array indexes. I.e ['group', 'myArray', '1', 'textField'].
userThe currently authenticated user object.

The condition function should return a boolean that will control if the field should be displayed or not.

Example:

ts
{
  fields: [
    {
      name: 'enableGreeting',
      type: 'checkbox',
      defaultValue: false,
    },
    {
      name: 'greeting',
      type: 'text',
      admin: {
        // highlight-start
        condition: (data, siblingData, { blockData, path, user }) => {
          if (data.enableGreeting) {
            return true
          } else {
            return false
          }
        },
        // highlight-end
      },
    },
  ]
}

Custom Components

Within the Admin Panel, fields are represented in three distinct places:

  • Field - The actual form field rendered in the Edit View.
  • Cell - The table cell component rendered in the List View.
  • Filter - The filter component rendered in the List View.
  • Diff - The Diff component rendered in the Version Diff View

To swap in Field Components with your own, use the admin.components property in your Field Config:

ts
import type { CollectionConfig } from 'payload'

export const CollectionConfig: CollectionConfig = {
  // ...
  fields: [
    // ...
    {
      // ...
      admin: {
        components: {
          // highlight-line
          // ...
        },
      },
    },
  ],
}

The following options are available:

ComponentDescription
FieldThe form field rendered of the Edit View. More details.
CellThe table cell rendered of the List View. More details.
FilterThe filter component rendered in the List View. More details.
LabelOverride the default Label of the Field Component. More details.
ErrorOverride the default Error of the Field Component. More details.
DiffOverride the default Diff component rendered in the Version Diff View. More details.
DescriptionOverride the default Description of the Field Component. More details.
beforeInputAn array of elements that will be added before the input of the Field Component. More details.
afterInputAn array of elements that will be added after the input of the Field Component. More details.

Field

The Field Component is the actual form field rendered in the Edit View. This is the input that user's will interact with when editing a document.

To swap in your own Field Component, use the admin.components.Field property in your Field Config:

ts
import type { CollectionConfig } from 'payload'

export const CollectionConfig: CollectionConfig = {
  // ...
  fields: [
    // ...
    {
      // ...
      admin: {
        components: {
          Field: '/path/to/MyFieldComponent', // highlight-line
        },
      },
    },
  ],
}

For details on how to build Custom Components, see Building Custom Components.

<Banner type="warning"> Instead of replacing the entire Field Component, you can alternately replace or slot-in only specific parts by using the [`Label`](#label), [`Error`](#error), [`beforeInput`](#afterinput-and-beforinput), and [`afterInput`](#afterinput-and-beforinput) properties. </Banner>
Default Props

All Field Components receive the following props by default:

PropertyDescription
docPreferencesAn object that contains the Preferences for the document.
fieldIn Client Components, this is the sanitized Client Field Config. In Server Components, this is the original Field Config. Server Components will also receive the sanitized field config through theclientField prop (see below).
localeThe locale of the field. More details.
readOnlyA boolean value that represents if the field is read-only or not.
userThe currently authenticated user. More details.
validateA function that can be used to validate the field.
pathA string representing the direct, dynamic path to the field at runtime, i.e. myGroup.myArray.0.myField.
schemaPathA string representing the direct, static path to the Field Config, i.e. posts.myGroup.myArray.myField.
indexPathA hyphen-notated string representing the path to the field within the nearest named ancestor field, i.e. 0-0

In addition to the above props, all Server Components will also receive the following props:

PropertyDescription
clientFieldThe serializable Client Field Config.
fieldThe Field Config.
dataThe current document being edited.
i18nThe i18n object.
payloadThe Payload class.
permissionsThe field permissions based on the currently authenticated user.
siblingDataThe data of the field's siblings.
userThe currently authenticated user. More details.
valueThe value of the field at render-time.
Sending and receiving values from the form

When swapping out the Field component, you are responsible for sending and receiving the field's value from the form itself.

To do so, import the useField hook from @payloadcms/ui and use it to manage the field's value:

tsx
'use client'
import { useField } from '@payloadcms/ui'

export const CustomTextField: React.FC = () => {
  const { value, setValue } = useField() // highlight-line

  return <input onChange={(e) => setValue(e.target.value)} value={value} />
}
<Banner type="success"> For a complete list of all available React hooks, see the [Payload React Hooks](../admin/react-hooks) documentation. For additional help, see [Building Custom Components](../custom-components/overview#building-custom-components). </Banner>
TypeScript#field-component-types

When building Custom Field Components, you can import the client field props to ensure type safety in your component. There is an explicit type for the Field Component, one for every Field Type and server/client environment. The convention is to prepend the field type onto the target type, i.e. TextFieldClientComponent:

tsx
import type {
  TextFieldClientComponent,
  TextFieldServerComponent,
  TextFieldClientProps,
  TextFieldServerProps,
  // ...and so on for each Field Type
} from 'payload'

See each individual Field Type for exact type imports.

Cell

The Cell Component is rendered in the table of the List View. It represents the value of the field when displayed in a table cell.

To swap in your own Cell Component, use the admin.components.Cell property in your Field Config:

ts
import type { Field } from 'payload'

export const myField: Field = {
  name: 'myField',
  type: 'text',
  admin: {
    components: {
      Cell: '/path/to/MyCustomCellComponent', // highlight-line
    },
  },
}

Example: Custom Cell Component

ts
'use client'
import type { DefaultCellComponentProps } from 'payload'

export const MyCustomCellComponent: React.FC<DefaultCellComponentProps> = ({
  cellData,
  field,
  rowData,
}) => {
  const price = cellData as number
  const currency = rowData.currency || 'USD'

  return (
    <span>
      {new Intl.NumberFormat('en-US', {
        style: 'currency',
        currency,
      }).format(price)}
    </span>
  )
}
<Banner type="warning"> **Important:** Use `DefaultCellComponentProps` for client components (with `'use client'` directive) and `DefaultServerCellComponentProps` for server components. Omitting `'use client'` will change the expected prop types. </Banner>

All Cell Components receive the following props:

PropertyDescription
cellDataThe data value for this specific cell.
collectionSlugThe slug of the collection being rendered.
fieldThe field configuration object.
rowDataAn object containing all data for the current row.
classNameOptional CSS class name for styling the cell.
columnIndexOptional index number of the column.
customCellPropsOptional additional custom props passed to the cell.
linkOptional boolean representing whether this cell should be wrapped in a link.
linkURLOptional URL string for the link.
onClickOptional function called when the cell is clicked.
viewTypeOptional string representing the type of view being rendered.
<Banner type="success"> **Note:** Cell components receive a different set of props than Field components, optimized specifically for rendering data in the List View. They do not receive the standard Field component props like `docPreferences`, `validate`, or `path`. </Banner>

For details on how to build Custom Components themselves, see Building Custom Components.

Filter

The Filter Component is the actual input element rendered within the "Filter By" dropdown of the List View used to represent this field when building filters.

All Custom Filter Components receive the following props:

PropertyDescription
disabledBoolean indicating whether the filter is disabled.
onChangeFunction to call when the filter value changes.
operatorThe comparison operator being used for the filter.
valueThe current value of the filter.

To swap in your own Filter Component, use the admin.components.Filter property in your Field Config:

ts
import type { Field } from 'payload'

export const myField: Field = {
  name: 'myField',
  type: 'text',
  admin: {
    components: {
      Filter: '/path/to/MyCustomFilterComponent', // highlight-line
    },
  },
}

All Custom Filter Components receive the same Default Field Component Props.

For details on how to build Custom Components themselves, see Building Custom Components.

Label

The Label Component is rendered anywhere a field needs to be represented by a label. This is typically used in the Edit View, but can also be used in the List View and elsewhere.

To swap in your own Label Component, use the admin.components.Label property in your Field Config:

ts
import type { Field } from 'payload'

export const myField: Field = {
  name: 'myField',
  type: 'text',
  admin: {
    components: {
      Label: '/path/to/MyCustomLabelComponent', // highlight-line
    },
  },
}

All Custom Label Components receive the same Default Field Component Props.

For details on how to build Custom Components themselves, see Building Custom Components.

TypeScript#label-component-types

When building Custom Label Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Label Component, one for every Field Type and server/client environment. The convention is to append LabelServerComponent or LabelClientComponent to the type of field, i.e. TextFieldLabelClientComponent.

tsx
import type {
  TextFieldLabelServerComponent,
  TextFieldLabelClientComponent,
  // ...and so on for each Field Type
} from 'payload'

Description

Alternatively to the Description Property, you can also use a Custom Component as the Field Description. This can be useful when you need to provide more complex feedback to the user, such as rendering dynamic field values or other interactive elements.

To add a Description Component to a field, use the admin.components.Description property in your Field Config:

ts
import type { CollectionConfig } from 'payload'

export const MyCollectionConfig: CollectionConfig = {
  // ...
  fields: [
    // ...
    {
      name: 'myField',
      type: 'text',
      admin: {
        components: {
          Description: '/path/to/MyCustomDescriptionComponent', // highlight-line
        },
      },
    },
  ],
}

All Custom Description Components receive the same Default Field Component Props.

For details on how to build a Custom Components themselves, see Building Custom Components.

TypeScript#description-component-types

When building Custom Description Components, you can import the component props to ensure type safety in your component. There is an explicit type for the Description Component, one for every Field Type and server/client environment. The convention is to append DescriptionServerComponent or DescriptionClientComponent to the type of field, i.e. TextFieldDescriptionClientComponent.

tsx
import type {
  TextFieldDescriptionServerComponent,
  TextFieldDescriptionClientComponent,
  // And so on for each Field Type
} from 'payload'

Error

The Error Component is rendered when a field fails validation. It is typically displayed beneath the field input in a visually-compelling style.

To swap in your own Error Component, use the admin.components.Error property in your Field Config:

ts
import type { Field } from 'payload'

export const myField: Field = {
  name: 'myField',
  type: 'text',
  admin: {
    components: {
      Error: '/path/to/MyCustomErrorComponent', // highlight-line
    },
  },
}

All Error Components receive the Default Field Component Props.

For details on how to build Custom Components themselves, see Building Custom Components.

TypeScript#error-component-types

When building Custom Error Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Error Component, one for every Field Type and server/client environment. The convention is to append ErrorServerComponent or ErrorClientComponent to the type of field, i.e. TextFieldErrorClientComponent.

tsx
import type {
  TextFieldErrorServerComponent,
  TextFieldErrorClientComponent,
  // And so on for each Field Type
} from 'payload'

Diff

The Diff Component is rendered in the Version Diff view. It will only be visible in entities with versioning enabled,

To swap in your own Diff Component, use the admin.components.Diff property in your Field Config:

ts
import type { Field } from 'payload'

export const myField: Field = {
  name: 'myField',
  type: 'text',
  admin: {
    components: {
      Diff: '/path/to/MyCustomDiffComponent', // highlight-line
    },
  },
}

All Error Components receive the Default Field Component Props.

For details on how to build Custom Components themselves, see Building Custom Components.

TypeScript#diff-component-types

When building Custom Diff Components, you can import the component types to ensure type safety in your component. There is an explicit type for the Diff Component, one for every Field Type and server/client environment. The convention is to append DiffServerComponent or DiffClientComponent to the type of field, i.e. TextFieldDiffClientComponent.

tsx
import type {
  TextFieldDiffServerComponent,
  TextFieldDiffClientComponent,
  // And so on for each Field Type
} from 'payload'

afterInput and beforeInput

With these properties you can add multiple components before and after the input element, as their name suggests. This is useful when you need to render additional elements alongside the field without replacing the entire field component.

To add components before and after the input element, use the admin.components.beforeInput and admin.components.afterInput properties in your Field Config:

ts
import type { CollectionConfig } from 'payload'

export const MyCollectionConfig: CollectionConfig = {
  // ...
  fields: [
    // ...
    {
      name: 'myField',
      type: 'text',
      admin: {
        components: {
          // highlight-start
          beforeInput: ['/path/to/MyCustomComponent'],
          afterInput: ['/path/to/MyOtherCustomComponent'],
          // highlight-end
        },
      },
    },
  ],
}

All afterInput and beforeInput Components receive the same Default Field Component Props.

For details on how to build Custom Components, see Building Custom Components.

TypeScript

You can import the Payload Field type as well as other common types from the payload package. More details.

ts
import type { Field } from 'payload'