Skip to content

Field Types ​

Contentrain provides 27 field types for defining model schemas. Each type includes built-in validation, constraints, and serialization rules.

ts
type FieldType =
  | 'string' | 'text' | 'email' | 'url' | 'slug' | 'color' | 'phone' | 'code' | 'icon'
  | 'markdown' | 'richtext'
  | 'number' | 'integer' | 'decimal' | 'percent' | 'rating'
  | 'boolean'
  | 'date' | 'datetime'
  | 'image' | 'video' | 'file'
  | 'relation' | 'relations'
  | 'select' | 'array' | 'object'

Field Definition Schema ​

Every field in a model is defined using the FieldDef interface:

ts
interface FieldDef {
  type: FieldType          // Required: the field type
  required?: boolean       // Validation: must have a value
  unique?: boolean         // Validation: unique within the collection
  default?: unknown        // Default value when not provided
  min?: number             // Minimum (length for strings, value for numbers)
  max?: number             // Maximum (length for strings, value for numbers)
  pattern?: string         // Regex validation pattern
  options?: string[]       // Allowed values (for select type)
  model?: string | string[] // Target model(s) for relation types
  items?: string | FieldDef // Item type for array fields
  fields?: Record<string, FieldDef> // Sub-fields for object type
  accept?: string          // MIME types for media fields
  maxSize?: number         // Max file size in bytes for media fields
  description?: string     // Human-readable field description
}

Complete Field Type Reference ​

Text Group ​

Field types for textual content of varying lengths and formats.

TypeDescriptionConstraintsStorage
stringShort text, single linemin, max (length), patternstring
textMulti-line plain textmin, max (length)string
emailEmail addressAuto-validated formatstring
urlWeb URLAuto-validated formatstring
slugURL-friendly identifierLowercase, hyphens, no spacesstring
codeSource code or preformatted textmin, max (length)string
iconIcon identifier (e.g., icon library name)patternstring

Examples ​

json
{
  "title": {
    "type": "string",
    "required": true,
    "min": 1,
    "max": 200
  },
  "bio": {
    "type": "text",
    "max": 1000,
    "description": "Author biography"
  },
  "contact_email": {
    "type": "email",
    "required": true
  },
  "website": {
    "type": "url"
  },
  "post_slug": {
    "type": "slug",
    "required": true,
    "unique": true
  },
  "snippet": {
    "type": "code"
  },
  "category_icon": {
    "type": "icon",
    "pattern": "^[a-z0-9-]+$"
  }
}

Rich Content Group ​

Field types for formatted content with markup.

TypeDescriptionConstraintsStorage
markdownMarkdown-formatted textmin, max (length)string
richtextHTML/rich text contentmin, max (length)string

Examples ​

json
{
  "content": {
    "type": "markdown",
    "required": true,
    "description": "Article body in Markdown format"
  },
  "formatted_bio": {
    "type": "richtext",
    "max": 5000
  }
}

TIP

For document-kind models, use the body key in content data for the main Markdown content rather than a markdown field. The markdown field type is for additional rich text fields within any model kind.

Numeric Group ​

Field types for numbers with different precision and display semantics.

TypeDescriptionConstraintsStorage
numberGeneral number (integer or float)min, max (value)number
integerWhole number onlymin, max (value)number
decimalFloating-point numbermin, max (value)number
percentPercentage value (0-100)min, max (value, defaults 0-100)number
ratingStar/score ratingmin, max (value, e.g., 1-5)number

Examples ​

json
{
  "price": {
    "type": "decimal",
    "required": true,
    "min": 0
  },
  "quantity": {
    "type": "integer",
    "min": 0,
    "max": 10000,
    "default": 0
  },
  "discount": {
    "type": "percent",
    "min": 0,
    "max": 100
  },
  "user_rating": {
    "type": "rating",
    "min": 1,
    "max": 5
  },
  "sort_order": {
    "type": "number",
    "default": 0
  }
}

Boolean ​

Simple true/false toggle.

TypeDescriptionConstraintsStorage
booleanTrue or falsedefaultboolean

Example ​

json
{
  "is_featured": {
    "type": "boolean",
    "default": false
  },
  "published": {
    "type": "boolean",
    "required": true
  }
}

Date/Time Group ​

Field types for temporal data.

TypeDescriptionConstraintsStorage
dateDate only (no time)min, max (ISO date strings)string (ISO 8601)
datetimeFull date and timemin, max (ISO datetime strings)string (ISO 8601)

Examples ​

json
{
  "publish_date": {
    "type": "date",
    "required": true
  },
  "event_time": {
    "type": "datetime"
  }
}

INFO

Dates are stored as ISO 8601 strings: "2026-03-15" for date, "2026-03-15T14:30:00Z" for datetime.

Media Group ​

Field types for file uploads and media references.

TypeDescriptionConstraintsStorage
imageImage file referenceaccept, maxSizestring (path)
videoVideo file referenceaccept, maxSizestring (path)
fileGeneric file referenceaccept, maxSizestring (path)

Examples ​

json
{
  "cover_image": {
    "type": "image",
    "required": true,
    "accept": "image/png,image/jpeg,image/webp",
    "maxSize": 5242880,
    "description": "Cover image (max 5MB)"
  },
  "intro_video": {
    "type": "video",
    "accept": "video/mp4,video/webm",
    "maxSize": 52428800
  },
  "resume": {
    "type": "file",
    "accept": "application/pdf",
    "maxSize": 10485760
  }
}

TIP

Media paths reference files in the project's assets_path (configured in config.json). The accept field uses standard MIME types.

Color ​

Color value field.

TypeDescriptionConstraintsStorage
colorColor valuepattern (e.g., hex format)string

Example ​

json
{
  "brand_color": {
    "type": "color",
    "required": true,
    "pattern": "^#[0-9a-fA-F]{6}$",
    "default": "#000000"
  }
}

Phone ​

Phone number field.

TypeDescriptionConstraintsStorage
phonePhone numberpatternstring

Example ​

json
{
  "contact_phone": {
    "type": "phone",
    "pattern": "^\\+[1-9]\\d{1,14}$",
    "description": "E.164 format phone number"
  }
}

Relation Group ​

Field types for connecting entries across models.

TypeDescriptionConstraintsStorage
relationSingle relation to another entrymodel (target model ID or array)string (entry ID); { model, ref } when model names more than one target
relationsMultiple relations to other entriesmodel (target model ID or array)string[] (entry IDs); { model, ref }[] when model names more than one target

Examples ​

json
{
  "author": {
    "type": "relation",
    "required": true,
    "model": "team-members",
    "description": "Post author"
  },
  "tags": {
    "type": "relations",
    "model": "tags",
    "description": "Associated tags"
  },
  "related_content": {
    "type": "relations",
    "model": ["blog-posts", "case-studies"],
    "description": "Related items from multiple collections"
  }
}

A relation whose model lists several targets is polymorphic: an entry ID alone does not say which model it belongs to, so the reference stores both — { "model": "case-studies", "ref": "a1b2c3d4e5f6" }. A one-element model array is a single target and stores the plain ID. validateFieldValue and contentrain_validate both apply this rule.

WARNING

Relation fields store entry IDs as references. Use the resolve parameter in contentrain_content_list to expand relations to full entry data at query time.

Complex Group ​

Field types for structured and compound data.

TypeDescriptionConstraintsStorage
selectSingle choice from predefined optionsoptions (required)string
arrayOrdered list of itemsitems (type or FieldDef), min, maxarray
objectNested object with sub-fieldsfields (Record of FieldDef)object

Examples ​

json
{
  "status": {
    "type": "select",
    "required": true,
    "options": ["active", "inactive", "coming-soon"],
    "default": "active"
  },
  "features": {
    "type": "array",
    "items": "string",
    "min": 1,
    "max": 10,
    "description": "List of feature names"
  },
  "gallery": {
    "type": "array",
    "items": {
      "type": "object",
      "fields": {
        "src": { "type": "image", "required": true },
        "caption": { "type": "string" },
        "alt": { "type": "string", "required": true }
      }
    }
  },
  "seo": {
    "type": "object",
    "fields": {
      "meta_title": { "type": "string", "max": 60 },
      "meta_description": { "type": "text", "max": 160 },
      "og_image": { "type": "image" },
      "no_index": { "type": "boolean", "default": false }
    }
  }
}

INFO

The array type can hold simple values (items: "string") or complex objects (items: { type: "object", fields: {...} }). Declare fields on an object to type its sub-fields — fields on any other type is rejected by model_save. Nesting is capped at 10 levels.

Complete Type Summary Table ​

#TypeGroupJS StorageSupports min/maxSupports patternSupports optionsSupports modelSupports items/fields
1stringTextstringLengthYesNoNoNo
2textTextstringLengthYesNoNoNo
3emailTextstringLengthAutoNoNoNo
4urlTextstringLengthAutoNoNoNo
5slugTextstringLengthAutoNoNoNo
6codeTextstringLengthYesNoNoNo
7iconTextstringLengthYesNoNoNo
8markdownRich ContentstringLengthYesNoNoNo
9richtextRich ContentstringLengthYesNoNoNo
10numberNumericnumberValueNoNoNoNo
11integerNumericnumberValueNoNoNoNo
12decimalNumericnumberValueNoNoNoNo
13percentNumericnumberValueNoNoNoNo
14ratingNumericnumberValueNoNoNoNo
15booleanBooleanbooleanNoNoNoNoNo
16dateDate/TimestringLengthYesNoNoNo
17datetimeDate/TimestringLengthYesNoNoNo
18imageMediastringNoNoNoNoNo
19videoMediastringNoNoNoNoNo
20fileMediastringNoNoNoNoNo
21colorColorstringLengthYesNoNoNo
22phonePhonestringLengthYesNoNoNo
23relationRelationstringNoNoNoYesNo
24relationsRelationstring[]CountNoNoYesNo
25selectComplexstringNoNoYes (required)NoNo
26arrayComplexarrayCountNoNoNoitems
27objectComplexobjectNoNoNoNofields

How to read min/max and pattern

Both are enforced generically at validation time: min/max compare string length, numeric value, or array count depending on the stored value, and pattern runs against any string value. The table marks the types where they are meaningful — a pattern on an image path is technically enforced too, it is just rarely what you want. There is no date-range semantics: on date/datetime fields min/max compare string length, not chronology.

Constraint Properties Reference ​

PropertyApplicable TypesDescription
requiredAllField must have a non-null value
uniqueAll (collection and document)Value must be unique across all entries — rejected on singleton models (model_save errors), N/A for dictionary (no fields)
defaultAllDefault value declared on the schema (must be coherent with the type/options)
minAll string types, numeric types, array, relationsMinimum length (strings), value (numbers), or count (arrays/relations)
maxAll string types, numeric types, array, relationsMaximum length (strings), value (numbers), or count (arrays/relations)
patternAll string types (primary use: string, icon, color, phone)Regular expression, enforced on every string value; must compile or model_save rejects it
optionsselect onlyArray of allowed string values — declaring it on any other type is a model_save error
modelrelation, relationsTarget model ID(s) for the relation — required for these types
itemsarrayItem type: a type name string or a nested FieldDef — items are validated by the same rules as a scalar of that type
acceptimage, video, fileAllowed formats — checked against the file extension only, as a warning that never blocks; unknown extensions stay silent
maxSizeimage, video, fileMaximum file size in bytes — accepted but never enforced by MCP (it stores a path, not the file); your media provider enforces it at ingest, and model_save returns a schema_warnings entry saying so
fieldsobjectSub-field definitions as Record<string, FieldDef>
descriptionAllHuman-readable description of the field
labelAllEditor label. A string is one label for every locale; an object keyed by locale ({ "en": "Body", "tr": "Gövde" }) carries a translation each. Absent means the field name is the label.
orderAllDisplay position, ascending. Fields without one sort last, alphabetically — so a model that declares none behaves as it always has. Fractional values allow inserting between two fields without renumbering.

How Constraints Are Enforced ​

Two distinct gates apply, at different moments:

At model_save (authoring time). The schema itself is validated and a misplaced constraint is a blocking error, not a silent no-op: options on a non-select, items on a non-array, fields on a non-object, accept/maxSize on a non-media field, unique on a singleton, min > max, an uncompilable pattern, a select without non-empty options, a relation/relations without model, a default incoherent with the type. Model IDs must be kebab-case, field names snake_case (^[a-z][a-z0-9_]*$), and unknown keys are rejected — a typo'd requird: true is an error. The snake_case rule applies to fields that are new: a field the model already has under a legacy name (creativeWork, from a project created before the rule) is kept and reported as a schema_warning, so a legacy model never becomes read-only. contentrain validate lists such fields as notices.

At contentrain_content_save (write time). Values are validated before anything is committed — a severity: error on any entry in the call means nothing is written. Severities split by intent:

  • Errors (block the write): wrong type, required missing, min/max violations, pattern mismatch, options membership, and definitional semantic checks — an invalid slug, an unparseable date/datetime, a non-integer integer, an out-of-range percent.
  • Warnings (pass through, reported in the response): heuristic semantic checks — email, url, color, phone formats — and the extension-based accept check. rating has no range enforcement by design.

Shared Across Contentrain AI and Studio

These 27 field types define content schemas used identically in both local workflows (@contentrain/mcp, CLI) and Contentrain Studio. The pure validators live in @contentrain/types (validateFieldValue, validateSemanticType), so Studio forms and MCP enforce the same contract. Content modeled locally works seamlessly in team workflows without changes.