Skip to content

Content Models

Content models define the schema for your content. Every piece of content in Studio follows a model definition that specifies what fields are available, their types, and their constraints.

Four Model Kinds

Studio supports four distinct model kinds, each optimized for a different data pattern.

The content sidebar: models grouped by domain, each showing its kind icon, entry count, and an i18n globe for localized models

Collection

A collection holds multiple entries sharing the same schema. Each entry has a unique ID and can be individually created, updated, and deleted.

Use for: Blog posts, team members, products, FAQs, events, testimonials.

json
{
  "id": "blog-posts",
  "name": "Blog Posts",
  "kind": "collection",
  "domain": "blog",
  "i18n": true,
  "fields": {
    "title": { "type": "string", "required": true },
    "slug": { "type": "slug", "required": true, "unique": true },
    "excerpt": { "type": "text" },
    "cover": { "type": "image" },
    "author": { "type": "relation", "model": "team-members" },
    "published_at": { "type": "date" }
  }
}

Content format: Object map of entries keyed by entry ID:

json
{
  "a1b2c3d4e5f6": {
    "title": "Getting Started",
    "slug": "getting-started",
    "excerpt": "Learn the basics...",
    "published_at": "2026-03-15"
  }
}

Singleton

A singleton holds exactly one entry per locale. There is no list view — just a single document to edit.

Use for: Site settings, homepage hero, footer configuration, SEO defaults.

json
{
  "id": "site-settings",
  "name": "Site Settings",
  "kind": "singleton",
  "domain": "system",
  "i18n": false,
  "fields": {
    "site_name": { "type": "string", "required": true },
    "logo": { "type": "image" },
    "tagline": { "type": "text" },
    "social_links": {
      "type": "array",
      "items": {
        "type": "object",
        "fields": {
          "platform": { "type": "string" },
          "url": { "type": "url" }
        }
      }
    }
  }
}

Content format: Flat object of field values (no entry IDs):

json
{
  "site_name": "My Website",
  "tagline": "Build something great",
  "logo": "media/original/logo.webp"
}

Document

A document stores Markdown content with typed frontmatter. The fields define the frontmatter schema, while the body is a full Markdown document.

Use for: Documentation pages, blog articles, long-form content, knowledge base entries.

Documents are identified by a slug rather than a generated ID. The body field holds the Markdown content, and all other fields become frontmatter.

Content format: Frontmatter fields plus a separate body property for the Markdown.

Dictionary

A dictionary stores flat key-value pairs. No field definitions are needed — all content is free-form string keys mapped to string values.

Use for: UI translations, configuration maps, feature flags, i18n string files.

json
{
  "id": "ui-strings",
  "name": "UI Strings",
  "kind": "dictionary",
  "domain": "system",
  "i18n": true
}

Content format:

json
{
  "nav.home": "Home",
  "nav.about": "About Us",
  "footer.copyright": "2026 My Company"
}

TIP

Dictionaries do not require field definitions. The fields property is omitted entirely. All values must be strings.

Field Types

Studio supports 27 field types across six categories:

Text Fields

TypeDescriptionExample
stringSingle-line textTitle, name
textMulti-line text (textarea)Description, excerpt
emailEmail address with validationContact email
urlURL with validationWebsite link
slugURL-safe identifier, auto-generatedURL path segment
colorHex color valueBrand color
phonePhone numberContact number
codeCode snippet (monospaced)Embed code
iconIcon identifierMenu icon

Rich Content Fields

TypeDescriptionExample
markdownMarkdown contentArticle body
richtextRich text (WYSIWYG)Formatted description

Number Fields

TypeDescriptionExample
numberGeneric numberQuantity, sort order
integerWhole numberCount, position
decimalDecimal numberPrice, weight
percentPercentage (0-100)Discount rate
ratingRating value (1-5)Review score

Boolean Field

TypeDescriptionExample
booleanTrue/false toggleFeatured, published

Date Fields

TypeDescriptionExample
dateDate only (YYYY-MM-DD)Published date
datetimeDate and time (ISO 8601)Event start time

Media Fields

TypeDescriptionExample
imageImage file referenceCover photo, avatar
videoVideo file referenceHero video
fileGeneric file referencePDF download

Relational Fields

TypeDescriptionExample
relationSingle reference to another entryPost author
relationsArray of referencesPost categories

Relation configuration:

json
{
  "author": {
    "type": "relation",
    "model": "team-members"
  },
  "categories": {
    "type": "relations",
    "model": "categories"
  }
}

Polymorphic relations (multiple target models):

json
{
  "related_items": {
    "type": "relations",
    "model": ["blog-posts", "pages"]
  }
}

Structural Fields

TypeDescriptionExample
selectDropdown with predefined optionsStatus, category
arrayList of itemsTags, features
objectNested key-value structureAddress, SEO config

Select field:

json
{
  "status": {
    "type": "select",
    "options": ["draft", "published", "archived"]
  }
}

Array field (simple):

json
{
  "tags": {
    "type": "array",
    "items": "string"
  }
}

Array field (objects):

json
{
  "features": {
    "type": "array",
    "items": {
      "type": "object",
      "fields": {
        "title": { "type": "string" },
        "icon": { "type": "icon" }
      }
    }
  }
}

Object field (max 2 levels deep):

json
{
  "address": {
    "type": "object",
    "fields": {
      "street": { "type": "string" },
      "city": { "type": "string" },
      "zip": { "type": "string" }
    }
  }
}

Field Options

Each field can have these configuration options:

OptionTypeDescription
requiredbooleanWhether the field must have a value
uniquebooleanWhether values must be unique across entries
minnumberMinimum value (numbers) or length (strings)
maxnumberMaximum value (numbers) or length (strings)
patternstringRegex pattern for validation
defaultanyDefault value when creating new entries
descriptionstringHelp text shown in the editor
optionsstring[]Valid options for select fields
modelstring or string[]Target model(s) for relation fields
itemsstring or objectItem schema for array fields
fieldsobjectNested field definitions for object fields

Domains

Models are organized into domains — logical groups that represent content areas. Common domains include:

  • blog — Blog posts, authors, categories
  • marketing — Landing pages, testimonials, pricing
  • system — Site settings, navigation, UI strings
  • docs — Documentation pages, API reference

Domains are purely organizational. They determine the directory structure in your repository (.contentrain/content/{domain}/{modelId}/) but have no functional impact on how content behaves.

Creating Models

Via AI Chat

The easiest way to create a model is through the AI chat:

"Create a collection model called 'Team Members' in the marketing domain with name (required string), role (string), bio (text), avatar (image), and email (email)"

The agent will create the model definition with the correct field schemas and commit it to a review branch.

Via Content Panel

You can also create models from the content panel UI by navigating to the project overview and using the model creation form.

Multi-Locale Support (i18n)

Models can opt into multi-locale support by setting i18n: true. When enabled:

  • Separate content files are created per locale (e.g., en.json, tr.json)
  • The content panel shows a locale switcher
  • The agent can copy content between locales using the copy_locale tool

Locales are configured at the project level during initialization. A default locale is always required.

TIP

Dictionary models with i18n enabled are ideal for managing UI translations — each locale gets its own set of key-value pairs.

Model Configuration

Model definitions live in .contentrain/models/ as JSON files. Each model file contains:

json
{
  "id": "blog-posts",
  "name": "Blog Posts",
  "kind": "collection",
  "domain": "blog",
  "i18n": true,
  "description": "Articles published on the company blog",
  "fields": {
    "title": { "type": "string", "required": true },
    "slug": { "type": "slug", "required": true, "unique": true }
  }
}
PropertyRequiredDescription
idYesKebab-case unique identifier
nameYesHuman-readable display name
kindYesOne of: collection, singleton, document, dictionary
domainYesContent domain (directory name)
i18nNoEnable multi-locale support (default: false)
descriptionNoDescription shown in the UI
fieldsVariesField definitions (required for all kinds except dictionary)

Modifying Models

You can update existing models through the AI chat or by editing the model JSON files directly. When using the agent:

"Add a 'featured' boolean field to the Blog Posts model"

The agent will update the model definition and commit the change to a review branch.

WARNING

Removing fields from a model does not automatically remove the corresponding data from existing content entries. The orphaned data will be ignored but remains in the content files until cleaned up.

Next Steps

Released under the AGPL-3.0 License.