5.4 KiB
5.4 KiB
| title | aliases | tags | sources | created | updated | ||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| PayloadCMS — Array Field |
|
|
|
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 afieldsarray to define repeating row structure minRows/maxRowsadd validation bounds without custom codelocalized: trueon the array localizes all sub-fields — don't set it per sub-fieldunique: trueinside an array = collection-wide DB index, not per-row uniqueness; usevalidateinsteadinterfaceNamegenerates a reusable TypeScript interface for the row shape- Custom
RowLabelviaadmin.components.RowLabel+useRowLabelhook for dynamic row headings - Arrays can be nested inside arrays for complex nested data
Related
- wiki/payloadcms/fields-complex — all complex field types overview
- wiki/payloadcms/fields-basic — scalar field reference
- wiki/payloadcms/database-indexes — unique index behavior and gotchas
- wiki/payloadcms/localization — how
localized: trueworks across field types - wiki/payloadcms/typescript —
interfaceNameand type generation - wiki/payloadcms/hooks — field lifecycle hooks
Sources
raw/fields__array.md— https://payloadcms.com/docs/fields/array