156 lines
5.2 KiB
Markdown
156 lines
5.2 KiB
Markdown
---
|
|
title: "Select Field"
|
|
aliases: [select-field, multi-select, dropdown-field]
|
|
tags: [payloadcms, fields, select, enum, dropdown]
|
|
sources: [raw/fields__select.md]
|
|
created: 2026-05-15
|
|
updated: 2026-05-15
|
|
---
|
|
|
|
Dropdown-style field storing one or many values from a predefined list. Backed by a database enum (Postgres) or string field.
|
|
|
|
## Basic Config
|
|
|
|
```ts
|
|
import type { Field } from 'payload'
|
|
|
|
export const MySelectField: Field = {
|
|
name: 'status',
|
|
type: 'select',
|
|
options: [
|
|
{ label: 'Draft', value: 'draft' },
|
|
{ label: 'Published', value: 'published' },
|
|
{ label: 'Archived', value: 'archived' },
|
|
],
|
|
}
|
|
```
|
|
|
|
## Config Options
|
|
|
|
| Option | Required | Description |
|
|
|--------|----------|-------------|
|
|
| `name` | ✓ | DB property name |
|
|
| `options` | ✓ | Array of strings or `{ label, value }` objects |
|
|
| `hasMany` | | `true` → allows multiple selections (stored as array) |
|
|
| `filterOptions` | | Dynamically filter visible/saveable options |
|
|
| `enumName` | | Custom Postgres enum name (auto-generated if omitted) |
|
|
| `dbName` | | Custom join table name when `hasMany: true` (Postgres) |
|
|
| `unique` | | DB-level unique constraint |
|
|
| `index` | | Add index for faster queries |
|
|
| `localized` | | Per-locale values |
|
|
| `saveToJWT` | | Include value in auth JWT |
|
|
| `defaultValue` | | Default option value |
|
|
| `required` | | Require a selection |
|
|
| `hidden` | | Exclude from all APIs (still stored in DB) |
|
|
| `virtual` | | Disable DB storage, or link to relationship |
|
|
| `interfaceName` | | Reusable TS interface + GraphQL type |
|
|
| `typescriptSchema` | | Override TS type generation with JSON schema |
|
|
|
|
> **GraphQL constraint:** option values must not contain hyphens or special characters — only underscores allowed. Values with special characters are reformatted automatically for GraphQL enums.
|
|
|
|
## hasMany — Multi-Select
|
|
|
|
```ts
|
|
{
|
|
name: 'selectedFeatures',
|
|
type: 'select',
|
|
hasMany: true,
|
|
admin: {
|
|
isClearable: true,
|
|
isSortable: true, // drag-and-drop reordering
|
|
},
|
|
options: [
|
|
{ label: 'Metallic Paint', value: 'metallic_paint' },
|
|
{ label: 'Alloy Wheels', value: 'alloy_wheels' },
|
|
{ label: 'Carbon Fiber Dashboard', value: 'carbon_fiber_dashboard' },
|
|
],
|
|
}
|
|
```
|
|
|
|
- `hasMany: true` stores an array of values
|
|
- `isSortable` only works when `hasMany: true`
|
|
- Postgres creates a separate join table (`dbName` to override its name)
|
|
|
|
## filterOptions
|
|
|
|
Dynamically filters which options appear in the Admin UI **and** which can be saved:
|
|
|
|
```ts
|
|
{
|
|
type: 'select',
|
|
options: [
|
|
{ label: 'One', value: 'one' },
|
|
{ label: 'Two', value: 'two' },
|
|
{ label: 'Three', value: 'three' },
|
|
],
|
|
filterOptions: ({ options, data }) =>
|
|
data.disallowOption1
|
|
? options.filter(o => (typeof o === 'string' ? o : o.value) !== 'one')
|
|
: options,
|
|
}
|
|
```
|
|
|
|
Use cases:
|
|
- Admin-only options based on `req.user.role`
|
|
- Dependent dropdowns (city filtered by selected state)
|
|
|
|
> Unlike `filterOptions` on [[wiki/payloadcms/fields-relationship|Relationship]] / [[wiki/payloadcms/fields-overview|Upload]] fields, the return value here is an **array of options**, not a query constraint.
|
|
|
|
## Admin Options
|
|
|
|
| Property | Description |
|
|
|----------|-------------|
|
|
| `isClearable` | Show a clear/reset button in the UI |
|
|
| `isSortable` | Enable drag-and-drop reordering (requires `hasMany: true`) |
|
|
| `placeholder` | Custom placeholder text or function |
|
|
|
|
## Custom Components
|
|
|
|
### Server Component (Field)
|
|
|
|
```tsx
|
|
import type { SelectFieldServerComponent } from 'payload'
|
|
import { SelectField } from '@payloadcms/ui'
|
|
|
|
export const CustomSelectFieldServer: SelectFieldServerComponent = ({
|
|
clientField, path, schemaPath, permissions,
|
|
}) => (
|
|
<SelectField field={clientField} path={path} schemaPath={schemaPath} permissions={permissions} />
|
|
)
|
|
```
|
|
|
|
### Client Component (Field)
|
|
|
|
```tsx
|
|
'use client'
|
|
import type { SelectFieldClientComponent } from 'payload'
|
|
import { SelectField } from '@payloadcms/ui'
|
|
|
|
export const CustomSelectFieldClient: SelectFieldClientComponent = (props) => (
|
|
<SelectField {...props} />
|
|
)
|
|
```
|
|
|
|
### Label Components
|
|
|
|
Use `FieldLabel` from `@payloadcms/ui` for both server and client label overrides — pass `label`, `path`, and `required` from `clientField`/`field`.
|
|
|
|
## Key Takeaways
|
|
|
|
- `options` values must use underscores, not hyphens (GraphQL enum constraint)
|
|
- `hasMany: true` switches storage to an array; Postgres creates a join table
|
|
- `filterOptions` controls both UI display and what values can be saved — useful for role-based or dependent dropdowns
|
|
- `enumName` / `dbName` let you control Postgres schema naming explicitly
|
|
- `isSortable` drag-and-drop only activates when `hasMany: true`
|
|
- Use `interfaceName` to generate a reusable TypeScript interface and GraphQL type for this field's enum
|
|
|
|
## Related
|
|
|
|
- [[wiki/payloadcms/fields-radio|Radio Field]] — single-choice alternative with radio-button UI
|
|
- [[wiki/payloadcms/fields-relationship|Relationship Field]] — link to other documents (also has `filterOptions`)
|
|
- [[wiki/payloadcms/fields-overview|Fields Overview]] — shared field config, validation, admin options
|
|
- [[wiki/payloadcms/database-postgres|Database — Postgres]] — enum and join table generation details
|
|
|
|
## Sources
|
|
|
|
- `raw/fields__select.md` — https://payloadcms.com/docs/fields/select
|