obsidian/wiki/payloadcms/fields-array.md
2026-05-15 15:22:27 +01:00

5.4 KiB

title aliases tags sources created updated
PayloadCMS — Array Field
payload-array-field
array-field-payload
payloadcms
fields
array
cms
typescript
raw/fields__array.md
2026-05-15 2026-05-15

Overview

The Array Field stores repeating sets of fields as an array of objects. Each row contains the same field structure you define. Arrays can be nested inside other arrays for infinitely deep data structures.

Common use cases:

  • Image slider (upload + caption text)
  • Navigation items (relationship + checkbox "open in new tab")
  • Event agenda timeslots (date + label + relationship)
import type { Field } from 'payload'

export const MyArrayField: Field = {
  name: 'slider',
  type: 'array',
  fields: [
    { name: 'title', type: 'text' },
    { name: 'image', type: 'upload', relationTo: 'media', required: true },
    { name: 'caption', type: 'text' },
  ],
}

Config Options

Option Required Description
name yes DB property name
fields yes Field definitions for each row
minRows no Minimum row count during validation
maxRows no Maximum row count during validation
labels no Customize singular/plural row labels in Admin
defaultValue no Array of row objects as default
localized no Localizes entire array (no need to localize each sub-field)
required no Field must have a value
validate no Custom validation function (runs client + server)
saveToJWT no Include in JWT if top-level in auth collection
hooks no Field lifecycle hooks
access no Field-level access control
hidden no Hide from APIs/Admin but still save to DB
interfaceName no Reusable TypeScript interface + GraphQL type name
dbName no Custom Postgres table name (auto-generated otherwise)
virtual no Disable DB persistence or link to a relationship path
typescriptSchema no Override generated TS type with a JSON schema

Admin Options

admin: {
  initCollapsed: true,        // start rows collapsed
  isSortable: false,          // disable drag-to-reorder (default: true)
  components: {
    RowLabel: './RowLabel',   // custom React component per row
  },
}

Custom Row Label

'use client'
import { useRowLabel } from '@payloadcms/ui'

export const ArrayRowLabel = () => {
  const { data, rowNumber } = useRowLabel<{ title?: string }>()
  return <div>{data.title || 'Slide'} {String(rowNumber).padStart(2, '0')}</div>
}

Gotchas

unique: true on nested fields

Setting unique: true on a field inside an array creates a collection-wide unique index — not per-document. Two documents cannot share the same value at that nested path. On MongoDB, documents without the array collide on null.

Fix: Use a custom validate function on the array field to enforce uniqueness within a single document's rows.

Localization

When localized: true on the array itself, all sub-fields are localized automatically. No need to mark each nested field.

Full Example

import type { CollectionConfig } from 'payload'

export const ExampleCollection: CollectionConfig = {
  slug: 'example-collection',
  fields: [
    {
      name: 'slider',
      type: 'array',
      label: 'Image Slider',
      minRows: 2,
      maxRows: 10,
      interfaceName: 'CardSlider',
      labels: { singular: 'Slide', plural: 'Slides' },
      fields: [
        { name: 'title', type: 'text' },
        { name: 'image', type: 'upload', relationTo: 'media', required: true },
        { name: 'caption', type: 'text' },
      ],
    },
  ],
}

Custom Components

Server Component (Field)

import { ArrayField } from '@payloadcms/ui'
import type { ArrayFieldServerComponent } from 'payload'

export const CustomArrayFieldServer: ArrayFieldServerComponent = ({
  clientField, path, schemaPath, permissions,
}) => <ArrayField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />

Client Component (Field)

'use client'
import { ArrayField } from '@payloadcms/ui'
import type { ArrayFieldClientComponent } from 'payload'

export const CustomArrayFieldClient: ArrayFieldClientComponent = (props) => <ArrayField {...props} />

Key Takeaways

  • Use type: 'array' with a fields array to define repeating row structure
  • minRows / maxRows add validation bounds without custom code
  • localized: true on the array localizes all sub-fields — don't set it per sub-field
  • unique: true inside an array = collection-wide DB index, not per-row uniqueness; use validate instead
  • interfaceName generates a reusable TypeScript interface for the row shape
  • Custom RowLabel via admin.components.RowLabel + useRowLabel hook for dynamic row headings
  • Arrays can be nested inside arrays for complex nested data

Sources