Content Lists
Developer guide

Make a component data-aware

How to opt your component into content-list binding so marketers can wire its props to records.

1. Update schema.json

Add supportsBinding: true to any prop you want marketers to bind. Optionally restrict the compatible field types with compatibleBindingTypes:

{
  "type": "blog-post-hero",
  "displayName": "Blog Post Hero",
  "props": {
    "title": {
      "type": "string",
      "supportsBinding": true,
      "compatibleBindingTypes": ["text"]
    },
    "coverImage": {
      "type": "image",
      "supportsBinding": true,
      "compatibleBindingTypes": ["image"]
    }
  }
}

2. Build and sync

Use the Mirin desktop app to build, preview, sync, and publish your local theme changes. The schema validator catches common mistakes before components reach marketers, such as compatibleBindingTypes set without supportsBinding, or an unknown prop type.

schema.json
supportsBinding
Build
desktop app
Validate
schema check
Sync up
to cloud
Marketers
can bind it

3. The marketer-facing experience

Each bound prop shows a chain-link icon in the property panel. Marketers click it, pick a list, pick a record, then pick a field. The renderer resolves the binding at SSR time and your component receives the literal value.

title (your prop)
Bind to a field
List: Events
Record: Summer Tasting
Field: Title
// your component receives:
props.title = "Summer Tasting"

Rendering a list with content_list

For repeater-style components (a blog index, team grid, testimonials carousel) declare a content_list prop:

{
  "props": {
    "source": {
      "type": "content_list",
      "settings": {
        "collectionSlug": "blog-posts",
        "limit": 12,
        "sort": "published_at:desc"
      }
    }
  }
}

Your component’s render signature gains a second items argument:

export function BlogPostGrid(props, items) {
  return (
    <ul>
      {items.map((item) => (
        <li key={item.id}>{item.data.title}</li>
      ))}
    </ul>
  );
}

Items are sanitised and coerced before your component sees them. Do not do your own escaping. The renderer pipeline already handles it.

Allowed sort keys: created_at:desc/asc, updated_at:desc/asc, published_at:desc/asc. Snake_case only. CamelCase keys like publishedAt:desc are rejected by the API.

Field type reference

Field typeValue shape
text / richtextstring
numbernumber
booleanboolean
dateISO 8601 string
image{ url, alt } after coercion
url / emailstring (validated scheme for url)
colorlowercase hex only: #rrggbb or #rrggbbaa
selectstring from the configured enum
referencerecord id string; resolve via internal /resolve
slugURL-safe string (can be computed from another field)

Troubleshooting

Binding shows the prop default instead of the record value.

Check the dashboard’s Health view. The binding is probably broken (record deleted, field removed, or cross-tenant).

“Reads from” chip is empty.

Your content_list is missing settings.collectionSlug. The desktop app catches this before the theme is synced.

Marketer cannot bind a number to a URL prop.

Working as designed. Set compatibleBindingTypes: ["url"] if the prop should accept URL fields.