All skills
cosmicjs avatar

/cosmic-content-modeling

@b2c4ac8

Design Cosmic CMS object types and metafields - choosing metafield types, validation rules, relationships, repeaters, rich-text fields, and conditional visibility. Use when creating or changing a Cosmic content model or schema, when the user asks what field type to use, when adding fields to an existing object type, when modeling relationships between content, or when a metafield value renders incorrectly in the UI.

  • 1 file
  • 7.8 KB
  • MIT
  • Updated 2 months ago
  • GitHub

Use this Skill: https://skilld.dev/gh/cosmicjs/cosmic-agent-plugin/cosmic-content-modeling

This session only. Nothing lands on disk.

SKILL.md

≈111 tokens always: the name and description. ≈1.9k when used: this file.

Cosmic content modeling

Object types define the shape of content in Cosmic, an AI-powered headless CMS. Each object type owns a list of metafields, and every object of that type carries values for them under metadata.

For reading and writing content once the model exists, see the cosmic-sdk skill.

Object types

// List all object types
const types = await cosmic.objectTypes.find()

// Get one object type
const blogType = await cosmic.objectTypes.findOne('posts')

// Create an object type with metafields
await cosmic.objectTypes.insertOne({
  title: 'Blog Posts',
  slug: 'posts',
  singular: 'Post',
  emoji: '📝',
  metafields: [
    { title: 'Content', key: 'content', type: 'markdown', required: true },
    { title: 'Image', key: 'image', type: 'file', media_validation_type: 'image' },
    { title: 'Author', key: 'author', type: 'object', object_type: 'authors' },
    { title: 'Tags', key: 'tags', type: 'objects', object_type: 'tags' },
  ],
})

The slug is the identifier used everywhere else. When creating objects you pass type: 'posts', the slug, never the title.

Metafield types

Type Description Value format
text Single line text string
textarea Multi-line text string
markdown Markdown editor string
rich-text Rich text editor (preferred for long-form) string (markdown + tokens)
html-textarea Rich HTML editor (deprecated, use rich-text) string
number Numeric value number
date Date picker "YYYY-MM-DD"
switch Boolean toggle true / false
select Single selection (preferred) string
multi-select Multiple selection string[]
select-dropdown Dropdown selection (deprecated, use select) { key: string, value: string }
radio-buttons Radio selection string
check-boxes Multiple selection string[]
file Single media Media name
files Multiple media Media name[]
object Single relation Object id
objects Multiple relations Object id[]
json JSON data object
color Color picker "#hex"
emoji Emoji picker string (the emoji character)
repeater Repeatable group array
parent Nested group object

Rich text

A rich-text value is markdown prose plus optional {{name /}} block tokens that reference blocks already defined in the bucket. It may also embed an existing object inline:

{{object type="type-slug" id="OBJECT_ID" slug="object-slug" /}}

Only reference objects that already exist. Never invent ids. object and objects are reserved block names.

Prefer rich-text over the deprecated html-textarea for new long-form fields.

Validation

Metafields support validation properties that enforce data quality at write time:

Property Type Description
required boolean A value must be provided
unique boolean Value must be unique across all objects of the same type. Applies to top-level text, textarea, number, date, and select metafields. Not supported inside parent or repeater groups
show_when object Conditional visibility: { key, op, value }. Shows the field when a sibling field matches. Ops: eq, neq, exists, not_exists. Hidden fields skip required validation. Top-level only
regex string Restrict the value to a regular expression
regex_message string Message shown when regex validation fails
minlength number Minimum character length (text, textarea)
maxlength number Maximum character length (text, textarea)
min number Minimum value (number)
max number Maximum value (number)
helptext string Guidance shown to editors beneath the field
await cosmic.objectTypes.insertOne({
  title: 'Contacts',
  slug: 'contacts',
  metafields: [
    { title: 'Email', key: 'email', type: 'text', required: true, unique: true },
    { title: 'Name', key: 'name', type: 'text', required: true, minlength: 2 },
  ],
})

Relationships

Model a relationship with object (one) or objects (many) and pin it to a target type with object_type:

{ title: 'Author', key: 'author', type: 'object', object_type: 'authors' }
{ title: 'Tags', key: 'tags', type: 'objects', object_type: 'tags' }

Values are object ids, not slugs. When reading, use .depth(1) or higher to resolve the related object rather than getting back a bare id.

Select and multi-select values

For new content models, prefer select for single selection and multi-select for multiple selection over the legacy select-dropdown type.

  • select returns a plain string. No helper needed.
  • multi-select returns a string[]. No helper needed.
  • select-dropdown (deprecated) returns { key, value } objects, which cause "Objects are not valid as a React child" errors when rendered directly in JSX.
// select and multi-select: use the values directly
<span>{product.metadata?.status}</span>
{product.metadata?.tags?.map((tag) => <span key={tag}>{tag}</span>)}

If a project still has legacy select-dropdown fields, add this helper (for example in lib/cosmic.ts):

export function getMetafieldValue(field: unknown): string {
  if (field === null || field === undefined) return ''
  if (typeof field === 'string') return field
  if (typeof field === 'number' || typeof field === 'boolean') return String(field)
  if (typeof field === 'object' && field !== null && 'value' in field) {
    return String((field as { value: unknown }).value)
  }
  if (typeof field === 'object' && field !== null && 'key' in field) {
    return String((field as { key: unknown }).key)
  }
  return ''
}

The function passes strings, numbers, and booleans through unchanged, so it is safe to wrap any metadata value rendered in JSX:

// Wrong: crashes if the field is a select-dropdown
<span>{product.metadata?.category}</span>

// Correct: safe for legacy select-dropdown
<span>{getMetafieldValue(product.metadata?.category)}</span>

Modeling guidance

  1. Slug is the contract. Renaming an object type slug or a metafield key breaks every query and template that references it. Choose deliberately.
  2. Prefer select over select-dropdown and rich-text over html-textarea in new models. Both legacy types still work but return awkward shapes.
  3. Reach for repeater before a json field. A repeater stays editable in the dashboard; a raw JSON blob does not.
  4. Use parent to group related fields rather than prefixing keys by hand.
  5. Put required only on fields that are genuinely required at creation time. A required field with no default blocks programmatic object creation.
  6. show_when is top-level only. Conditional visibility does not work inside repeater or parent groups.
  7. Schema changes need the write key, so run them from server-side code or a local script, never from the browser.

Resources

Source: SKILL.md on GitHub

No third-party reports yet.

Signed by skilld at b2c4ac8. This ties the file your Agent reads to that commit on GitHub. It does not review the instructions.

Last checked against GitHub 2 days ago.

Steadyupdated 2 months ago

README badge

README badge for cosmicjs/cosmic-agent-plugin/cosmic-content-modeling