---
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]]
