Field Types Reference

Complete reference for all built-in field types and their options.

Base options

All field types accept these common options:


          type BaseFieldOptions<TValue = unknown> = {
  title?: string;            // human-readable label shown in the admin UI instead of the field name
  description?: string;      // helper text rendered below the label in the admin UI
  required?: boolean;        // default: false
  searchable?: boolean;      // include in /api/search results (default: false; applies to text, slug, select)
  defaultValue?: TValue;
  validate?: (value: TValue) => true | string;
  admin?: {
    components?: FieldSlotComponents;
    // Both receive { value, data, user }. Return false to hide / disable.
    visible?: (ctx: AdminFieldCtx) => boolean;
    readOnly?: (ctx: AdminFieldCtx) => boolean;
  };
};

type AdminFieldCtx = {
  // `value` is intentionally `unknown` — TS can't narrow it across the
  // discriminated `AnyField` union when the admin invokes these callbacks.
  // Narrow inside the function body if you need a precise type.
  value: unknown;
  data: Record<string, unknown>;
  user: CmsUser;
};
        

text and number additionally accept unique?: boolean (default: false) to add a per-entity UNIQUE constraint on the column.

See Fields — Custom field components for the slot reference and usage examples.

text


          text(name: string, opts?: BaseFieldOptions<string> & {
  unique?: boolean;
  rows?: number; // > 1 renders a textarea in the admin
}): TextField
        
  • Storage: Column (text)
  • API value: string
  • Admin: defaults to a single-line <input>. Set rows greater than 1 to render a multi-line <textarea> with that many visible rows. Storage is unchanged either way.

          text("summary", { rows: 4 });
        

slug


          slug(name: string, opts?: BaseFieldOptions<string> & {
  unique?: 'entity' | 'global'  // default: 'entity'
}): SlugField
        
  • Storage: Column (text)
  • API value: string
  • Format: Must match /^[a-z0-9]+(?:-[a-z0-9]+)*$/ — lowercase alphanumeric segments separated by hyphens
  • Special: Supports $glob filter operator for path matching

Uniqueness scopes

| unique | Mechanism | Scope | | ---------- | --------- | ----- | | 'entity' (default) | Column UNIQUE constraint | Unique within this entity's table only | | 'global' | _slug registry table (PK + FK) | Unique across all entities |

Use 'global' when slugs must be unique site-wide (e.g. for a catch-all route). Use 'entity' (the default) when slugs only need to be unique per entity.

number


          number(name: string, opts?: BaseFieldOptions<number> & { unique?: boolean }): NumberField
        
  • Storage: Column (double precision)
  • API value: number

boolean


          boolean(name: string, opts?: BaseFieldOptions<boolean>): BooleanField
        
  • Storage: Column (boolean)
  • API value: boolean

date


          date(name: string, opts?: BaseFieldOptions<string>): DateField
        
  • Storage: Column (timestamptz)
  • API value: string (ISO 8601)

select


          type SelectOption = string | { label: string; value: string };

select<T extends readonly SelectOption[]>(
  name: string,
  opts: BaseFieldOptions<SelectOptionValue<T[number]>> & { options: T }
): SelectField<T>
        
  • Storage: Column (text)
  • API value: One of the specified option values
  • Type-safe: Option values are inferred as a literal union type
  • Two option forms: Pass plain strings ("draft") when the label matches the stored value, or { label, value } objects when you want a human-friendly label that differs from the DB value. The two forms can mix in a single list.

json


          json(name: string, opts?: BaseFieldOptions): JsonField
        
  • Storage: JSONB
  • API value: Any JSON value
  • Limitation: Cannot be used for sorting

richText


          richText(name: string, opts?: BaseFieldOptions & {
  styles?: RichTextFeature[];
  decorators?: RichTextFeature[];
  lists?: RichTextFeature[];
  annotations?: RichTextAnnotation[];
  blocks?: RichTextBlock[];
  inlineBlocks?: RichTextBlock[];
}): RichTextField
        
  • Storage: JSONB
  • API value: Array of Portable Text blocks
  • Admin: WYSIWYG editor with configurable styles, decorators, lists, annotations, blocks, and inline blocks
  • Frontend: Render with @portabletext/react or any Portable Text library

Sub-types


          // Styles, decorators, and lists — pass a plain string to use defaults
type RichTextFeature = string;

type RichTextAnnotation = {
  name: string;
  fields: AnyField[];
};

type RichTextBlock = {
  name: string;
  fields: AnyField[];
};
        

Factory helpers


          richText.annotation(name: string, opts: {
  fields: AnyField[];
}): RichTextAnnotation

richText.block(name: string, opts: {
  fields: AnyField[];
}): RichTextBlock

richText.inlineBlock(name: string, opts: {
  fields: AnyField[];
}): RichTextBlock
        

See the Fields guide for usage examples.

relation


          relation(name: string, opts: {
  to: EntityDef | string | Array<EntityDef | string>;
  multiple?: boolean;
} & BaseFieldOptions): RelationField
        
  • Storage: FK column (single) or junction table (multiple)
  • API value: UUID string (single), UUID array (multiple), or resolved object(s) with ?resolve

Single relation

Creates a {name}_id column with a foreign key constraint.

Multiple relation

Creates a {entity}_{name} junction table with sourceId, targetId, and position columns.

Polymorphic relation

When to is an array, the target entity type is stored alongside the ID.

image


          image(name: string, opts?: {
  multiple?: boolean;
} & BaseFieldOptions): RelationField
        
  • Storage: FK column (single) or junction table (multiple)
  • API value: UUID string (single) or UUID array (multiple)
  • Target: Built-in _image entity
  • Admin: Upload input with thumbnail preview, browse library, and remove controls

Shorthand for relation(name, { to: "_image", multiple? }). The admin UI renders a typed image input instead of the generic relation picker.

video


          video(name: string, opts?: {
  multiple?: boolean;
} & BaseFieldOptions): RelationField
        
  • Storage: FK column (single) or junction table (multiple)
  • API value: UUID string (single) or UUID array (multiple)
  • Target: Built-in _video entity
  • Admin: Upload input with filename preview, browse library, and remove controls

Shorthand for relation(name, { to: "_video", multiple? }).

file


          file(name: string, opts?: {
  multiple?: boolean;
} & BaseFieldOptions): RelationField
        
  • Storage: FK column (single) or junction table (multiple)
  • API value: UUID string (single) or UUID array (multiple)
  • Target: Built-in _file entity
  • Admin: Upload input with filename and size preview, browse library, and remove controls

Shorthand for relation(name, { to: "_file", multiple? }).

For fields that accept multiple asset types, use relation() directly with a polymorphic target:


          relation("media", { to: ["_image", "_video"] })
        

object


          object(name: string, opts: {
  fields: AnyField[];
} & BaseFieldOptions): ObjectField
        
  • Storage: JSONB
  • API value: Object matching the field structure

blocks


          blocks(name: string, opts: {
  of: ObjectField[];
} & BaseFieldOptions): BlocksField
        
  • Storage: JSONB (array)
  • API value: Array of objects, each with a _type discriminator

          [
  { "_type": "hero", "heading": "Welcome" },
  { "_type": "cta", "text": "Sign up", "url": "/signup" }
]
        

array


          array(name: string, opts: {
  of: AnyField;
} & BaseFieldOptions): ArrayField
        
  • Storage: JSONB
  • of: A single field definition — use a primitive field for flat arrays, or an object field for arrays of objects
  • API value: Array of primitives or array of objects, depending on of
  • Admin: Add/remove/reorder items with up/down buttons

Do not use array() to hold multiple references to another entity — use relation() with multiple: true instead. Wrapping a relation in an array stores IDs as JSONB, losing referential integrity, cross-entity filtering, and resolution. See Handling multiple relations.

Layout directives

Layout directives are admin-only — no storage, no migration impact.


          tabs(tabDefs: Array<{ label: string; fields: AnyField[] }>): TabsDirective

collapsible(
  label: string,
  fields: AnyField[],
  opts?: { defaultOpen?: boolean }  // default: true
): CollapsibleDirective

row(fields: AnyField[]): RowDirective
        

Directives can be nested and combined with regular fields in any fields array.

Previous

Client

Next

Config Reference