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.6+ Payload projects using the Next.js App Router in src/app with 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/* and dist/fields/* for .d.ts files that declare props, default behaviors, and helper exports.
Source inspection: Many components re-export JSX from nearby index.js files—quickly open them to confirm transitions, context requirements, or composed children.
CSS classes: Each folder ships a matching .scss file. 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.tsx shows 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 useField come 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'` programmaticSubmitbooleanForces 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 tooltip prop 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 titlestringRequired heading text. actionsReactNodeSlot for buttons/pills on the right. href / onClickstring / () => voidMake entire card interactive. titleAsElementTypeOverride 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 headerReactNodeAlways-visible summary content. initCollapsedbooleanInitial toggle state; defaults to collapsed. isCollapsedbooleanControl state externally. onToggle(collapsed: boolean) => voidSync external state or analytics. collapsibleStyle`'default' 'error'`
Usage
<Collapsible
className="array-field__row"
initCollapsed={false }
header={<span > {rowTitle}</span > }
>
{children}
</Collapsible >
Tips
Combine with dragHandleProps from DraggableSortable to 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 slugstringRequired unique identifier per drawer instance. titlestringHeader text; shown next to close icon. HeaderReactNodeReplace the standard header entirely. gutterbooleanAdds 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 formatDrawerSlug when 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[]` isMultibooleanEnable multi-value selection; returns Option[]. isCreatablebooleanAllow creating ad-hoc options. customPropsCustomSelectPropsHook 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 placeholder strings or LabelFunction to 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) => voidEmits JS Date objects. readOnlybooleanPrevent user edits but keep styling. placeholderstringGhost 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 DateTimeField when 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 idsstring[]Array of unique IDs matching child order. onDragEnd({ moveFromIndex, moveToIndex }) => voidRequired reorder logic. onDragStart({ id }) => voidOptional analytics/feedback. classNamestringApply 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-id attributes via the DraggableSortable item 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 columnsColumn[]Use Payload’s column definition shape (accessor, Header, etc.). dataRecord<string, unknown>[]Array of row objects keyed by column accessor. appearance`'default' 'condensed'` BeforeTableReactNodeRender filters/action bars above the table.
Usage
<Table
appearance="condensed"
columns={columns}
data={rows}
/>
Tips
For orderable tables, use the companion OrderableTable export 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 showbooleanControl visibility manually; defaults to hover trigger. delaynumberMilliseconds 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, including localized and required flags.
Pair with FieldDescription for 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 path is 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 showError flags 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 pathstringRequired unique path for Payload tracking. valuestringControlled input value. onChange(event) => voidStandard change handler (or ReactSelect change when hasMany). AfterInput / BeforeInputReactNodeSlots for adornments. localizedbooleanAdjusts 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 useField to sync values with Payload and pass the path consistently.
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 rowsnumberDefault row count; expand with CSS if needed. valuestringControlled text. onChange(ChangeEvent<HTMLTextAreaElement>) => voidUpdate 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 optionsOptionObject[]Usually from collection config. hasManybooleanEnables multi-select; returns string[]. isSortablebooleanExposes 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 relationTostring[]Collection slugs supported (mono or poly). appearance`'select' 'drawer'` allowCreate / allowEditbooleanEnables “Create new” and “Edit” buttons. hasManybooleanMulti-relation mode; returns arrays. filterOptionsFilterOptionsResultPre-filter server results.
Usage
<RelationshipField
path="product"
field={fieldConfig}
relationTo={['products' ]}
hasMany={false }
allowEdit
allowCreate
value={currentProduct}
onChange={setCurrentProduct}
/>
Tips
Use maxResultsPerRequest to 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 checkedbooleanControlled value. partialCheckedbooleanShow indeterminate state (e.g., bulk toggles). onChange(value: boolean) => voidEmit 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 DraggableSortable classes (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 BlocksDrawer and BlockRow live 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 fieldUploadFieldClientUse the config passed into custom components. pathstringUnique identifier for form state. onChangeProvided via Payload; typically not replaced manually.
Usage
<UploadField
path="galleryHero"
field={fieldConfig}
/>
Tips
Access the exported UploadInput directly for lightweight file pickers without the full field chrome.
Patterns & Best Practices
Sync with useField: For bespoke components, call const { value, setValue, showError } = useField({ path }) and pass showError to inputs that support it.
Respect path: Payload’s form state, localization, and validation rely on consistent path strings; do not reuse them across siblings.
Leverage CSS utilities: Copy existing class names (pill, gutter--left, toolbar, etc.) from .scss files to inherit spacing without bespoke CSS.
Accessibility: All elements supply ARIA hooks. Keep aria-label/htmlFor/aria-describedby attributes wired when wrapping components.
Live updates: When upgrading Payload, re-run pnpm install and diff node_modules/@payloadcms/ui/dist to spot new components or prop changes to mirror here.
This glossary is a reference companion to the Payload CMS custom admin UI components guide , which walks through the full workflow of discovering, importing, and building custom fields from scratch. To see these components in production on real Payload projects, explore the Payload CMS live demos .