Skip to Content
FeaturesField Types

Field Types

Fields are the building blocks of your content models. When you create a model, you add fields to define what data each record can hold.

Overview

Foir provides a set of built-in field types that cover the most common content needs. Each field type has its own editor experience, validation rules, and configuration options.

All fields support these common options:

OptionDescription
LabelDisplay name shown in the editor
KeyUnique identifier used in APIs (auto-generated from label)
RequiredMust be filled before saving/publishing
Help TextGuidance shown below the field in the editor
Default ValuePre-filled value for new records

Text Fields

Text

Single-line text for titles, names, and short content.

{ "key": "title", "type": "text", "label": "Title", "required": true }

Config options: minLength, maxLength, pattern (regex validation)

Translatable: Yes

Textarea

Multi-line plain text — descriptions, excerpts, anything that doesn’t need formatting.

{ "key": "excerpt", "type": "textarea", "label": "Excerpt" }

Translatable: Yes

Content

The unified rich-text + structured-blocks editor. Replaces both richtext and flexible as separate field types. One field, one editing surface, mixes prose and inline blocks.

{ "key": "body", "type": "content", "label": "Body" }

What “content” is

Stored as a Lexical EditorState (a JSON tree). At read time the platform resolves it into an array of typed segments your client can render directly. Each segment is one of:

_typeShapeWhat it is
richtext{ _type, _id, html, markdown, json }A run of prose grouped into one segment. The platform pre-renders both HTML and Markdown so consumers don’t need a Lexical renderer.
<inlineModelKey>{ _type, _id, ...resolvedFields }An inline block — a record of an inline-mode model embedded inline in the content. Fields are resolved through the normal field pipeline (so an image field on the block returns a full ImageValue, etc.).
media{ _type, _id, mediaType, url, ..., alt? }An embedded image, video, or file. mediaType is normalised from the file’s MIME, so legacy records with miscategorised media self-heal. Per-usage alt text is preserved.
reference{ _type, _id, _schema, modelKey, naturalKey, title?, record? }A reference to another record. record is a Record union resolving to the concrete user model type, so consumers can select typed fields inline.

Rendering on the client

query { page(naturalKey: "home") { body { __typename ... on RichTextBlock { html markdown } ... on MediaBlock { url alt mediaType } ... on ReferenceBlock { modelKey naturalKey record { ... on Author { name avatar { url } } ... on Product { title price } } } ... on Hero { headline ctaUrl } ... on FeatureGrid { features { title description icon { url } } } } } }
function RenderBody({ blocks }: { blocks: BodyBlock[] }) { return ( <> {blocks.map((b) => { switch (b.__typename) { case 'RichTextBlock': return <div key={b._id} dangerouslySetInnerHTML={{ __html: b.html }} />; case 'MediaBlock': return <Media key={b._id} {...b} />; case 'ReferenceBlock':return <Reference key={b._id} record={b.record} />; case 'Hero': return <Hero key={b._id} {...b} />; // ... } })} </> ); }

Block allowlist

By default, all inline-mode models in your project are available as blocks. Restrict to a subset with config.blocks:

{ "key": "body", "type": "content", "label": "Body", "config": { "blocks": ["hero", "feature-grid", "testimonial-card"] } }

Translatable

Yes — content fields support per-locale translations like richtext did. Variants apply at three levels (page / content-field / block); see Variants for the model.

Email

Email address field with built-in validation.

{ "key": "contactEmail", "type": "email", "label": "Contact Email" }

Translatable: No

Phone

Phone number field.

{ "key": "phone", "type": "phone", "label": "Phone Number" }

Translatable: No

URL

URL field with built-in validation.

{ "key": "website", "type": "url", "label": "Website" }

Translatable: No

Media Fields

Image

Upload and manage images with built-in metadata support. Returns an ImageValue object with url, width, height, alt, blurhash, dominantColor, mimeType, fileSize, focalPoint, and crops.

{ "key": "featuredImage", "type": "image", "label": "Featured Image" }

Features:

  • Alt text — Text for accessibility
  • Focal point — Set the important area for smart cropping
  • Crops — Pre-defined crop variants (square, landscape, etc.)
  • Blurhash — Automatically generated placeholder hash

Translatable: No (alt text is part of the image value)

Video

Upload videos with automatic processing. Returns a VideoValue object with url, thumbnailUrl, previewUrl, hlsManifestUrl, width, height, duration, mimeType, and fileSize.

{ "key": "heroVideo", "type": "video", "label": "Hero Video" }

Features:

  • Poster image — Thumbnail shown before playback
  • HLS streaming — Automatic manifest generation
  • Duration — Automatically detected on upload

Translatable: No

File

Any file type — PDFs, documents, spreadsheets, downloads. Returns a FileValue object with url, filename, mimeType, and size.

{ "key": "attachment", "type": "file", "label": "Attachment" }

Translatable: No

Number and Date Fields

Number

Numeric values for prices, quantities, ratings, and counters.

{ "key": "price", "type": "number", "label": "Price", "required": true }

Config options: min, max, step, decimals

Translatable: No

Currency

Monetary values with currency formatting.

{ "key": "totalPrice", "type": "currency", "label": "Total Price" }

Returns a CurrencyValue object.

Translatable: No

Date

Date picker for publish dates, event dates, deadlines, and scheduling.

{ "key": "publishDate", "type": "date", "label": "Publish Date" }

Config options: includeTime (enables time selection)

Translatable: No

Boolean

Yes/no toggle for featured flags, visibility settings, and switches.

{ "key": "isFeatured", "type": "boolean", "label": "Featured" }

Translatable: No

Selection Fields

Select

Choose a single option from a predefined list.

{ "key": "category", "type": "select", "label": "Category", "config": { "options": [ { "label": "News", "value": "news" }, { "label": "Tutorial", "value": "tutorial" }, { "label": "Case Study", "value": "case-study" } ] } }

Translatable: No

Multiselect

Choose multiple options from a predefined list.

{ "key": "tags", "type": "multiselect", "label": "Tags", "config": { "options": [ { "label": "Featured", "value": "featured" }, { "label": "Popular", "value": "popular" }, { "label": "New", "value": "new" } ] } }

Returns an array of selected values.

Translatable: No

Reference Fields

Reference

Link to another record. Use for authors, related posts, parent pages, and any content relationship.

{ "key": "author", "type": "reference", "label": "Author", "config": { "referenceTypes": ["team-member"] } }

Config options: referenceTypes (array of model keys to link to)

Translatable: No

Internal or external links with structured data.

{ "key": "cta", "type": "link", "label": "Call to Action" }

Features:

  • Link text — The visible label
  • URL or page reference — External URL or internal record reference
  • Open in new tab — Target behavior

Supports internal pages, external URLs, email (mailto:), and phone (tel:) links.

Translatable: Yes

Structured Data Fields

JSON

Arbitrary JSON data for complex or unstructured values.

{ "key": "metadata", "type": "json", "label": "Metadata" }

Translatable: No

List

Array of items for ordered collections.

{ "key": "bulletPoints", "type": "list", "label": "Bullet Points" }

Translatable: No

Translatable Fields Summary

Fields that support per-locale translations:

Field TypeTranslatable
TextYes
TextareaYes
ContentYes
LinkYes
EmailNo
PhoneNo
URLNo
ImageNo
VideoNo
FileNo
NumberNo
CurrencyNo
DateNo
BooleanNo
SelectNo
MultiselectNo
ReferenceNo
JSONNo
ListNo

See Localization for details on how translations work.

Migrating from richtext / flexible

The unified content field replaced two earlier separate types:

  • richtext — prose only, no inline blocks. Equivalent to a content field whose document happens to contain only prose nodes; the API still returns a single RichTextBlock segment.
  • flexible — array of blocks, no prose. Equivalent to a content field whose document contains only block / media / reference nodes interspersed with empty prose runs.

Existing records on the old types continue to resolve correctly while your data is migrated. New models should declare type: 'content'.

In the Admin

Adding fields to a model

  1. Go to Models
  2. Select or create a model
  3. Click Add Field
  4. Choose the field type
  5. Configure options (label, required, help text, type-specific settings)
  6. Save the model

Custom field types

You can create your own composite field types by defining a model with inline mode enabled. For example, create a “Button” model with text, URL, and style fields, then use it as a field type on any model.

See Inline Schemas for details.

Best Practices

  • Mark fields as required only when truly necessary. Optional fields give content teams more flexibility.
  • Use help text to guide editors on format expectations, character limits, or content guidelines.
  • Choose the most specific field type for the data. Use number for prices (not text), date for dates, and boolean for toggles.
  • Enable translations on fields that will need localized content. You can enable this later, but planning ahead saves migration effort.
  • Use references instead of duplicating content. Link to a shared “Author” record rather than entering the author name on every post.
Last updated on