obsidian/wiki/payloadcms/fields-basic.md
2026-05-15 15:13:56 +01:00

381 lines
11 KiB
Markdown

---
tags: [payloadcms, tech-patterns]
topic: payloadcms
sources:
- https://payloadcms.com/docs/fields/overview
- https://payloadcms.com/docs/fields/text
- https://payloadcms.com/docs/fields/textarea
- https://payloadcms.com/docs/fields/number
- https://payloadcms.com/docs/fields/email
- https://payloadcms.com/docs/fields/date
- https://payloadcms.com/docs/fields/checkbox
- https://payloadcms.com/docs/fields/code
- https://payloadcms.com/docs/fields/json
- https://payloadcms.com/docs/fields/point
- https://payloadcms.com/docs/fields/radio
- https://payloadcms.com/docs/fields/select
- https://payloadcms.com/docs/fields/ui
created: 2026-05-15
---
# PayloadCMS — Fields: Basic
## Overview
Scalar (basic) fields store a single value per document. They map directly to database columns and drive typed inputs in the Admin Panel. All fields live inside the `fields` array of a [[wiki/payloadcms/configuration|Configuration]] (Collection or Global).
See [[wiki/payloadcms/fields-complex|Fields: Complex]] for structural/relational fields.
## Field Types Quick Reference
| Field | Type string | Key options |
|-------|-------------|-------------|
| Text | `text` | `minLength`, `maxLength`, `hasMany`, `unique` |
| Textarea | `textarea` | `minLength`, `maxLength`, `admin.rows` |
| Number | `number` | `min`, `max`, `hasMany`, `admin.step` |
| Email | `email` | `unique`, `index` |
| Date | `date` | `timezone`, `admin.date.pickerAppearance` |
| Checkbox | `checkbox` | `defaultValue` (boolean) |
| Code | `code` | `admin.language`, `admin.editorOptions` |
| JSON | `json` | `jsonSchema` (local or remote) |
| Point | `point` | index defaults to `2dsphere`, not supported in SQLite |
| Radio | `radio` | `options` (required), `admin.layout` |
| Select | `select` | `options`, `hasMany`, `filterOptions`, `enumName` |
| UI | `ui` | `admin.components.Field` (required), presentational only |
## Common Field Options
These options are shared by almost all data-storing fields:
| Option | Type | Description |
|--------|------|-------------|
| `name` * | `string` | Property name in DB and API. Required for data fields. |
| `label` | `string \| Record<string,string>` | Admin Panel label, auto-generated from `name` if omitted. |
| `required` | `boolean` | Prevents saving without a value. |
| `defaultValue` | any | Initial value; can be a static value or async function. |
| `unique` | `boolean` | DB-level unique index across the collection. |
| `index` | `boolean` | Creates a DB index for faster queries. |
| `localized` | `boolean` | Stores a per-locale copy. Requires localization enabled globally. |
| `hidden` | `boolean` | Hides from all APIs and Admin; still saved to DB. |
| `validate` | `function` | Custom validation, runs on both client and server. |
| `hooks` | `object` | `beforeValidate`, `beforeChange`, `afterChange`, `afterRead`. |
| `access` | `object` | Field-level read/create/update access control functions. |
| `saveToJWT` | `boolean` | Include value in user JWT (auth collections only, top-level fields). |
| `virtual` | `boolean \| string` | `true` = not persisted to DB; string path = virtual relationship link. |
| `custom` | `object` | Arbitrary extension data for plugins. |
| `typescriptSchema` | `JSONSchema` | Override generated TypeScript type with a JSON schema. |
Admin-level sub-options available on all fields (via `admin: {}`):
| Option | Description |
|--------|-------------|
| `description` | Help text rendered below the field. |
| `condition` | `(data, siblingData) => boolean` — conditional rendering. |
| `readOnly` | Renders as read-only in Admin. |
| `disabled` | Disables the field UI (granular: `{ field?, column?, filter?, ... }`). |
| `position` | `'sidebar'` to move field to the document sidebar. |
| `width` | CSS width for use inside `row` fields (e.g. `'50%'`). |
| `components` | Override `Field`, `Label`, `Cell` React components. |
---
## Each Field
### Text
Stores a `string`. Most commonly used field.
```ts
import type { Field } from 'payload'
const titleField: Field = {
name: 'pageTitle',
type: 'text',
required: true,
minLength: 3,
maxLength: 120,
admin: {
placeholder: 'Enter page title...',
autoComplete: 'off',
rtl: false,
},
}
```
**Extra options:** `minLength`, `maxLength`, `hasMany` (array of strings), `minRows`, `maxRows` (when `hasMany`), `admin.placeholder`, `admin.autoComplete`, `admin.rtl`.
**Slug Field shortcut:** `import { slugField } from 'payload'` — auto-generates URL slug from another field with a lock/unlock UI.
**Gotcha:** `unique: true` creates a collection-wide DB index, not per-document uniqueness.
---
### Textarea
Nearly identical to Text but renders a taller input — use for descriptions, bios, meta.
```ts
const descField: Field = {
name: 'metaDescription',
type: 'textarea',
maxLength: 160,
admin: {
rows: 3,
placeholder: 'Short summary...',
},
}
```
**Extra options:** same as Text plus `admin.rows` (default `2`).
---
### Number
Stores a numeric value.
```ts
const priceField: Field = {
name: 'price',
type: 'number',
min: 0,
required: true,
admin: {
step: 0.01,
placeholder: '0.00',
},
}
```
**Extra options:** `min`, `max`, `hasMany`, `minRows`, `maxRows`, `admin.step`, `admin.placeholder`, `admin.autoComplete`.
---
### Email
Like Text but validates that the value is a valid email address.
```ts
const emailField: Field = {
name: 'contactEmail',
type: 'email',
required: true,
unique: true,
admin: {
placeholder: 'user@example.com',
},
}
```
No unique-to-email options beyond standard text options. Format validation is automatic.
---
### Date
Stores a full ISO datetime string (`YYYY-MM-DDTHH:mm:ss.SSSZ`). UI uses `react-datepicker`.
```ts
const publishedAtField: Field = {
name: 'publishedAt',
type: 'date',
required: true,
admin: {
date: {
pickerAppearance: 'dayAndTime', // dayAndTime | dayOnly | timeOnly | monthOnly
displayFormat: 'd MMM yyyy HH:mm',
timeIntervals: 15,
},
},
}
```
**Timezone support:**
```ts
{
name: 'eventTime',
type: 'date',
timezone: {
defaultTimezone: 'America/New_York',
supportedTimezones: [
{ label: 'New York', value: 'America/New_York' },
{ label: 'London', value: 'Europe/London' },
],
},
}
```
Timezone value is saved in a companion column `<fieldName>_tz`. Date is always stored in UTC — handle conversion in your frontend.
**`displayFormat`** only affects the cell display; the stored value is always full ISO.
---
### Checkbox
Stores a `boolean`.
```ts
const featuredField: Field = {
name: 'isFeatured',
type: 'checkbox',
defaultValue: false,
}
```
**Gotcha:** When `required: true`, `defaultValue` must be `true` — otherwise required validation will always fail on `false`.
---
### Code
Stores a `string` but renders a Monaco-editor code block in Admin.
```ts
const snippetField: Field = {
name: 'trackingCode',
type: 'code',
admin: {
language: 'javascript', // any Monaco language slug
editorOptions: { lineNumbers: 'off' },
},
}
```
**Gotcha:** Saves as raw string to DB — not parsed. Different from JSON field.
---
### JSON
Stores raw JSON (parsed object/array) in the DB. Also Monaco-editor UI.
```ts
const configField: Field = {
name: 'settings',
type: 'json',
jsonSchema: {
uri: 'a://schema/settings.json', // required — must be unique URI
fileMatch: ['a://schema/settings.json'], // required
schema: {
type: 'object',
properties: {
theme: { enum: ['light', 'dark'] },
},
},
},
}
```
`jsonSchema` enables typeahead in Admin and validates on save. Can also point to a remote URL — Payload fetches the schema.
**Gotcha:** Code field stores string; JSON field stores a parsed value — do not confuse them.
---
### Point
Stores GeoJSON `[longitude, latitude]` coordinates with an automatic `2dsphere` index.
```ts
const locationField: Field = {
name: 'location',
type: 'point',
}
```
Supports geospatial query operators: `near`, `within` (polygon), `intersects` (polygon).
**Gotcha:** Not supported in SQLite. Disabling the index (`index: false`) removes geospatial query support.
---
### Radio
Single-select from a predefined list. Renders a radio group.
```ts
const statusField: Field = {
name: 'status',
type: 'radio',
options: [
{ label: 'Draft', value: 'draft' },
{ label: 'Published', value: 'published' },
{ label: 'Archived', value: 'archived' },
],
defaultValue: 'draft',
admin: {
layout: 'horizontal', // horizontal | vertical
},
}
```
**Gotcha:** Option `value` strings must not contain hyphens — GraphQL enum constraint. Underscores are fine.
---
### Select
Dropdown single or multi-select from a predefined list.
```ts
const tagsField: Field = {
name: 'tags',
type: 'select',
hasMany: true,
options: [
{ label: 'News', value: 'news' },
{ label: 'Tutorial', value: 'tutorial' },
{ label: 'Release', value: 'release' },
],
admin: {
isClearable: true,
isSortable: true, // drag-and-drop reorder, only when hasMany
},
}
```
**`filterOptions`** — function receiving `{ options, data, siblingData, ... }` that returns a filtered options array (different from relationship `filterOptions` which returns a Where query).
**Extra options:** `enumName` (custom Postgres enum name), `dbName` (custom join table name when `hasMany`), `interfaceName`.
---
### UI
Presentational-only. No data stored. Renders a custom React component anywhere in the field schema.
```ts
const helpTextField: Field = {
name: 'myHelper',
type: 'ui',
admin: {
components: {
Field: '/path/to/MyHelpText', // required
Cell: '/path/to/MyHelpCell', // optional — list view column
},
},
}
```
Use cases: custom buttons (Refund, Clear Cache, View Page), contextual help text, action components.
**Gotcha:** `admin.components.Field` is required — without it nothing renders. Does not participate in validation or hooks.
---
## Gotchas
- **`unique: true` on nested fields** (inside Array/Group) creates a collection-wide index, not per-row uniqueness. Use a custom `validate` for per-row enforcement.
- **`hidden: true`** still persists data to DB — use field `access` to truly restrict reads.
- **`localized: true`** requires global localization config (`i18n`) to be enabled first.
- **`virtual: true`** prevents DB storage entirely; useful for computed/derived fields.
- **Radio/Select values** must not contain hyphens — GraphQL enum naming constraint.
- **Point field** is unsupported in SQLite databases.
- **Date timezone** column is auto-named `<fieldName>_tz`; date itself stored in UTC.
## Related
- [[wiki/payloadcms/fields-complex|Fields: Complex]]
- [[wiki/payloadcms/configuration|Configuration]]