Presets
Static preset palettes and async per-request preset resolvers.
Presets render as a labeled swatch grid in the picker popover. Picking one fills the field with the preset's value (or a reference to it in linked mode).
Static presets
colorField({
name: 'accent',
presets: [
'#0f172a',
{ key: 'brand', value: '#3b82f6', label: 'Brand' },
{ key: 'success', value: '#22c55e', label: { en: 'Success', de: 'Erfolg' } },
],
})A bare string is shorthand for a preset whose key and value are both the CSS string. Object presets carry a stable key (required for linked mode) and an optional localizable label.
Scheme-aware presets
A preset's value can be a light/dark pair instead of a single color, so one reference stays mode-responsive wherever it renders:
colorField({
name: 'accent',
linked: { resolve: 'schemes' },
presets: [{ key: 'brand', label: 'Brand', value: { light: '#0ea5e9', dark: '#0369a1' } }],
})Scheme and flat presets mix freely in one palette. The picker renders a scheme preset as a diagonal split swatch, so an editor sees the color is mode-responsive without visiting the site.
A pair with only one member filled in behaves as that color in both schemes, rather than disappearing from the picker while the palette is half authored. A pair with neither member filled in is not a preset and is skipped.
Linked mode covers what a document stores and what it resolves to.
Async presets
presets also accepts a resolver, for palettes that live in your data (a tenant's brand colors, a design-tokens global):
colorField({
name: 'accent',
presets: async ({ req }) => {
const { docs } = await req.payload.find({ collection: 'tenants', depth: 0, limit: 50 })
return docs.flatMap((tenant) =>
(tenant.brandColors ?? []).map((c) => ({ key: c.slug, value: c.color, label: c.name }))
)
},
})Resolvers are memoized per request, so a list view rendering fifty rows resolves the palette once, not fifty times. Because a single resolver result is shared across every document in a request, resolvers derive from req, not from a single document's data.
Plugin-level defaults
Presets and the stored format passed to the plugin apply to every color field that does not define its own:
fields({
color: {
format: 'oklch',
presets: [{ key: 'brand', value: '#3b82f6', label: 'Brand' }],
},
})Per-field presets replace the plugin default entirely (no merging), keeping the resolution rule predictable. At the plugin level, a static presets array takes precedence over an async resolvePresets. A field without its own format inherits color.format; per-field format always wins, and with neither set the stored format is hex.
Custom heading
presetsLabel replaces the default "Presets" heading and accepts any Payload static label (string or locale map).