Payload CMS Admin UI Components: Complete Glossary
Authoritative reference for @payloadcms/ui elements & fields (v3.6+). Props, imports, usage examples, and best…

📚 Comprehensive Payload CMS Guides
Detailed Payload guides with field configuration examples, custom components, and workflow optimization tips to speed up your CMS development process.
This living reference catalogues the high-usage UI primitives exposed by @payloadcms/ui so custom admin extensions can look native. It complements the walkthrough in docs/guides/building-custom-admin-ui-payload-cms-v3.md and will grow as we document additional components.
Using This Reference
- Scope: v3.5+ Payload projects using the Next.js App Router in
src/appwith custom admin extensions in(payload)routes. - Audience: developers building bespoke field types, layout chrome, and admin embeds who need parity with Payload styles and behavior.
- Maintenance: update version notes and props when bumping Payload; add new components as they enter regular use.
How to Explore @payloadcms/ui
- Type definitions: Browse
node_modules/@payloadcms/ui/dist/elements/*anddist/fields/*for.d.tsfiles that declare props, default behaviors, and helper exports. - Source inspection: Many components re-export JSX from nearby
index.jsfiles—quickly open them to confirm transitions, context requirements, or composed children. - CSS classes: Each folder ships a matching
.scssfile. Copy class names (e.g.,array-field,pill,tooltip) instead of inventing new selectors. - Local examples: The bulk inventory table component at
src/components/fields/InventoryBatchBulkTable.tsxshows how these pieces fit together in production.
Import Conventions
- Elements live under
@payloadcms/ui/elements/<Component>. - Field primitives live under
@payloadcms/ui/fields/<Component>. - Hooks such as
useFieldcome from the package root:import { useField } from '@payloadcms/ui'. - Most components are safe in both client and server components, but anything that depends on browser APIs (e.g., drag-and-drop, drawers) must be marked
'use client'at the file top.
Elements
Button (@payloadcms/ui/elements/Button)
Role: Primary action trigger with design-system variants and optional icons.
Import
import { Button } from '@payloadcms/ui/elements/Button'
Key props
| prop | type | notes |
|---|---|---|
buttonStyle | `'primary' | 'secondary' |
size | `'xsmall' | 'small' |
icon | `'chevron' | 'edit' |
iconPosition | `'left' | 'right'` |
programmaticSubmit | boolean | Forces form submission for detached buttons. |
secondaryActions | `secondaryAction | secondaryAction[]` |
Usage
<Button
buttonStyle="primary"
size="medium"
icon="plus"
iconPosition="left"
onClick={onAddRow}
>
Add another row
</Button>
Tips
- Prefer
buttonStyle="primary"for a single dominant action per block. - Use the
tooltipprop for icon-only buttons to preserve accessibility.
Card (@payloadcms/ui/elements/Card)
Role: Clickable summary tile with title and optional CTA area.
Import
import { Card } from '@payloadcms/ui/elements/Card'
Key props
| prop | type | notes |
|---|---|---|
title | string | Required heading text. |
actions | ReactNode | Slot for buttons/pills on the right. |
href / onClick | string / () => void | Make entire card interactive. |
titleAs | ElementType | Override rendered heading element (e.g., h3). |
Usage
<Card
title="Inventory batches"
actions={<Button buttonStyle="secondary">Open</Button>}
onClick={() => router.push('/admin/inventory')}
/>
Tips
- Cards inherit spacing from
index.scss; avoid extra padding wrappers unless necessary.
Collapsible (@payloadcms/ui/elements/Collapsible)
Role: Expandable section wrapper used inside array rows and grouped forms.
Import
import { Collapsible } from '@payloadcms/ui/elements/Collapsible'
Key props
| prop | type | notes |
|---|---|---|
header | ReactNode | Always-visible summary content. |
initCollapsed | boolean | Initial toggle state; defaults to collapsed. |
isCollapsed | boolean | Control state externally. |
onToggle | (collapsed: boolean) => void | Sync external state or analytics. |
collapsibleStyle | `'default' | 'error'` |
Usage
<Collapsible
className="array-field__row"
initCollapsed={false}
header={<span>{rowTitle}</span>}
>
{children}
</Collapsible>
Tips
- Combine with
dragHandlePropsfromDraggableSortableto make rows draggable while preserving toggles.
Drawer (@payloadcms/ui/elements/Drawer)
Role: Right-side overlay for nested editing (mirrors Payload relationship drawers).
Import
import { Drawer, DrawerToggler } from '@payloadcms/ui/elements/Drawer'
Key props
| prop | type | notes |
|---|---|---|
slug | string | Required unique identifier per drawer instance. |
title | string | Header text; shown next to close icon. |
Header | ReactNode | Replace the standard header entirely. |
gutter | boolean | Adds internal padding. |
Usage
<Drawer slug="product-selector" title="Select product" gutter>
<ProductList onSelect={handleSelect} />
</Drawer>
<DrawerToggler slug="product-selector">
<Button buttonStyle="secondary">Open drawer</Button>
</DrawerToggler>
Tips
- Wrap component file with
'use client'; drawers rely on window APIs. - Use
formatDrawerSlugwhen nesting drawers to avoid collisions.
Modal (@payloadcms/ui/elements/Modal)
Role: Faceless UI modal re-export for global overlays.
Import
import { Modal, useModal } from '@payloadcms/ui/elements/Modal'
Usage
const { toggleModal } = useModal()
<Modal slug="confirm-reset">
<div className="modal__content">
<h2>Reset filters?</h2>
<Button buttonStyle="primary" onClick={onConfirm}>Confirm</Button>
</div>
</Modal>
<Button onClick={() => toggleModal('confirm-reset', true)}>Reset filters</Button>
Tips
- Register modals once near the layout root to ensure portal context matches Payload’s admin shell.
ReactSelect (@payloadcms/ui/elements/ReactSelect)
Role: Styled react-select adapter used across select and relationship fields.
Import
import { ReactSelect, type Option } from '@payloadcms/ui/elements/ReactSelect'
Key props
| prop | type | notes |
|---|---|---|
options | `Option[] | OptionGroup[]` |
isMulti | boolean | Enable multi-value selection; returns Option[]. |
isCreatable | boolean | Allow creating ad-hoc options. |
customProps | CustomSelectProps | Hook into Payload’s document drawers for relationships. |
value | `Option | Option[]` |
Usage
<ReactSelect
options={products}
value={selectedProduct}
isSearchable
isClearable
onChange={(option) => setSelectedProduct(option as Option<number>)}
/>
Tips
- Pass
placeholderstrings orLabelFunctionto localize. - For full relationship behavior (create/edit drawers), prefer the Relationship field wrapper.
DatePickerField (@payloadcms/ui/elements/DatePicker)
Role: Calendar and time input matching Payload’s date fields.
Import
import { DatePickerField } from '@payloadcms/ui/elements/DatePicker'
Key props
| prop | type | notes |
|---|---|---|
value | `Date | string` |
onChange | (date: Date) => void | Emits JS Date objects. |
readOnly | boolean | Prevent user edits but keep styling. |
placeholder | string | Ghost text when empty. |
| Day/Time picker props | Inherits from Payload’s DayPickerProps and TimePickerProps. |
Usage
<DatePickerField
value={row.receivedDate}
onChange={(date) => updateDate(format(date))}
placeholder="Select received date"
/>
Tips
- Combine with
DateTimeFieldwhen you need server-managed validation and localization.
DraggableSortable (@payloadcms/ui/elements/DraggableSortable)
Role: Drag-and-drop wrapper for reorderable lists tied to @dnd-kit.
Import
import { DraggableSortable } from '@payloadcms/ui/elements/DraggableSortable'
Key props
| prop | type | notes |
|---|---|---|
ids | string[] | Array of unique IDs matching child order. |
onDragEnd | ({ moveFromIndex, moveToIndex }) => void | Required reorder logic. |
onDragStart | ({ id }) => void | Optional analytics/feedback. |
className | string | Apply Payload spacing helpers (array-field__draggable-rows). |
Usage
<DraggableSortable
ids={rows.map((row) => row.id)}
className="array-field__draggable-rows"
onDragEnd={({ moveFromIndex, moveToIndex }) => reorderRows(moveFromIndex, moveToIndex)}
>
{rows.map(renderRow)}
</DraggableSortable>
Tips
- Children should render
data-idattributes via theDraggableSortableitem helpers (see existing array field source) for smooth dragging.
Table (@payloadcms/ui/elements/Table)
Role: Minimal data table for list-style summaries.
Import
import { Table } from '@payloadcms/ui/elements/Table'
Key props
| prop | type | notes |
|---|---|---|
columns | Column[] | Use Payload’s column definition shape (accessor, Header, etc.). |
data | Record<string, unknown>[] | Array of row objects keyed by column accessor. |
appearance | `'default' | 'condensed'` |
BeforeTable | ReactNode | Render filters/action bars above the table. |
Usage
<Table
appearance="condensed"
columns={columns}
data={rows}
/>
Tips
- For orderable tables, use the companion
OrderableTableexport in the same folder.
Tooltip (@payloadcms/ui/elements/Tooltip)
Role: Hover/focus tooltip used for icon buttons and status pills.
Import
import { Tooltip } from '@payloadcms/ui/elements/Tooltip'
Key props
| prop | type | notes |
|---|---|---|
show | boolean | Control visibility manually; defaults to hover trigger. |
delay | number | Milliseconds before showing. |
position | `'top' | 'bottom'` |
alignCaret | `'left' | 'center' |
Usage
<Tooltip position="top" delay={150}>
<Button buttonStyle="icon-label" icon="info" aria-label="More info" />
</Tooltip>
Tips
- Wrap only the trigger element; Tooltip clones its child and wires up events.
Field Primitives
FieldLabel (@payloadcms/ui/fields/FieldLabel)
Role: Standardized label element with required indicator support.
Import
import { FieldLabel } from '@payloadcms/ui/fields/FieldLabel'
Usage
<FieldLabel
htmlFor="bulkRows"
label="Bulk inventory batches"
/>
Notes
- Accepts Payload’s
GenericLabelProps, includinglocalizedandrequiredflags. - Pair with
FieldDescriptionfor helper text.
FieldDescription (@payloadcms/ui/fields/FieldDescription)
Role: Helper text block with consistent spacing and subdued color.
Import
import { FieldDescription } from '@payloadcms/ui/fields/FieldDescription'
Usage
<FieldDescription
path="bulkRows"
description="Add one row per product variant."
/>
Notes
- The
pathis used by Payload tooling for error linking; keep it stable.
FieldError (@payloadcms/ui/fields/FieldError)
Role: Inline error message styled to match Payload validations.
Import
import { FieldError } from '@payloadcms/ui/fields/FieldError'
Usage
<FieldError
message="Quantity must be greater than zero"
/>
Notes
- Render conditionally when
showErrorflags trigger within field components.
TextInput & TextField (@payloadcms/ui/fields/Text)
Role: Single-line input primitive and full field wrapper tied to form state.
Import
import { TextInput } from '@payloadcms/ui/fields/Text'
Key props
| prop | type | notes |
|---|---|---|
path | string | Required unique path for Payload tracking. |
value | string | Controlled input value. |
onChange | (event) => void | Standard change handler (or ReactSelect change when hasMany). |
AfterInput / BeforeInput | ReactNode | Slots for adornments. |
localized | boolean | Adjusts layout when translating. |
placeholder | `string | Record<string, string>` |
Usage (controlled input)
<TextInput
path={`${row.id}-totalStock`}
value={row.totalStock ?? ''}
onChange={(event) => updateRow(row.id, event.target.value)}
placeholder="Enter quantity"
required
/>
Tips
- When building full custom fields, use
useFieldto sync values with Payload and pass thepathconsistently.
Textarea (@payloadcms/ui/fields/Textarea)
Role: Multi-line text area with Payload typography.
Import
import { TextareaInput } from '@payloadcms/ui/fields/Textarea'
Key props
| prop | type | notes |
|---|---|---|
rows | number | Default row count; expand with CSS if needed. |
value | string | Controlled text. |
onChange | (ChangeEvent<HTMLTextAreaElement>) => void | Update state. |
Usage
<TextareaInput
path="notes"
value={notes}
onChange={(event) => setNotes(event.target.value)}
placeholder="Optional notes"
/>
Select (@payloadcms/ui/fields/Select)
Role: Payload-managed select field leveraging ReactSelect styles.
Import
import { SelectInput } from '@payloadcms/ui/fields/Select'
Key props
| prop | type | notes |
|---|---|---|
options | OptionObject[] | Usually from collection config. |
hasMany | boolean | Enables multi-select; returns string[]. |
isSortable | boolean | Exposes drag handles for multi values. |
placeholder | `LabelFunction | string` |
Usage
<SelectInput
path="deliveryDate"
name="deliveryDate"
options={dateOptions}
value={currentDateId}
onChange={(option) => setCurrentDate(option?.value)}
/>
Relationship (@payloadcms/ui/fields/Relationship)
Role: Fetch-on-demand relation picker with optional drawer editing.
Import
import { RelationshipField } from '@payloadcms/ui/fields/Relationship'
Key props
| prop | type | notes |
|---|---|---|
relationTo | string[] | Collection slugs supported (mono or poly). |
appearance | `'select' | 'drawer'` |
allowCreate / allowEdit | boolean | Enables “Create new” and “Edit” buttons. |
hasMany | boolean | Multi-relation mode; returns arrays. |
filterOptions | FilterOptionsResult | Pre-filter server results. |
Usage
<RelationshipField
path="product"
field={fieldConfig}
relationTo={['products']}
hasMany={false}
allowEdit
allowCreate
value={currentProduct}
onChange={setCurrentProduct}
/>
Tips
- Use
maxResultsPerRequestto balance performance when large collections exist.
DateTime (@payloadcms/ui/fields/DateTime)
Role: Full Payload date field wrapper including timezone + validation.
Import
import { DateTimeField } from '@payloadcms/ui/fields/DateTime'
Usage
<DateTimeField
path="receivedDate"
field={fieldConfig}
value={valueFromForm}
onChange={setValue}
/>
Notes
- Automatically wires
DatePickerField; rely on it when you need Payload’s default min/max validation and timezone handling baked in.
Checkbox (@payloadcms/ui/fields/Checkbox)
Role: Styled checkbox with tri-state support.
Import
import { CheckboxField } from '@payloadcms/ui/fields/Checkbox'
Key props
| prop | type | notes |
|---|---|---|
checked | boolean | Controlled value. |
partialChecked | boolean | Show indeterminate state (e.g., bulk toggles). |
onChange | (value: boolean) => void | Emit selection state. |
Usage
<CheckboxField
path="confirm"
checked={isConfirmed}
onChange={setConfirmed}
field={fieldConfig}
/>
Array (@payloadcms/ui/fields/Array)
Role: Native array field wrapper with add/remove controls and draggable rows.
Import
import { ArrayField } from '@payloadcms/ui/fields/Array'
Usage
<ArrayField
path="bulkRows"
field={fieldConfig}
value={valueFromForm}
validate={fieldConfig.validate}
/>
Notes
- Combine with
DraggableSortableclasses (array-field,array-field__header,array-field__draggable-rows) when creating bespoke layouts that still resemble core arrays.
Blocks (@payloadcms/ui/fields/Blocks)
Role: Stackable content blocks with drawer-based block chooser.
Import
import { BlocksField } from '@payloadcms/ui/fields/Blocks'
Usage
<BlocksField
path="contentBlocks"
field={fieldConfig}
/>
Notes
- Components such as
BlocksDrawerandBlockRowlive in the same folder if you need deeper customization.
Upload (@payloadcms/ui/fields/Upload)
Role: File upload picker with thumbnail preview, relationship drawer, and server-driven validation.
Import
import { UploadField } from '@payloadcms/ui/fields/Upload'
Key props
| prop | type | notes |
|---|---|---|
field | UploadFieldClient | Use the config passed into custom components. |
path | string | Unique identifier for form state. |
onChange | Provided via Payload; typically not replaced manually. |
Usage
<UploadField
path="galleryHero"
field={fieldConfig}
/>
Tips
- Access the exported
UploadInputdirectly for lightweight file pickers without the full field chrome.
Patterns & Best Practices
- Sync with
useField: For bespoke components, callconst { value, setValue, showError } = useField({ path })and passshowErrorto inputs that support it. - Respect
path: Payload’s form state, localization, and validation rely on consistentpathstrings; do not reuse them across siblings. - Leverage CSS utilities: Copy existing class names (
pill,gutter--left,toolbar, etc.) from.scssfiles to inherit spacing without bespoke CSS. - Accessibility: All elements supply ARIA hooks. Keep
aria-label/htmlFor/aria-describedbyattributes wired when wrapping components. - Live updates: When upgrading Payload, re-run
pnpm installand diffnode_modules/@payloadcms/ui/distto spot new components or prop changes to mirror here.