Skip to main content
A collection is a group of entries that share the same shape — for example, “Blog Posts,” “Products,” or “Team Members.” Each collection has a single schema (its fields) and lives in a single directory on disk. Most sites need three to ten collections; some need only one. This page covers what a collection is, the three ways to create one, naming and format conventions, references between collections, and ready-to-use patterns for common content types.

What’s in a Collection?

Every collection in your manifest has:
  • An id — the stable identifier (blog, products, case-studies)
  • A name — the human label shown in the CMS panel
  • A path — where entries live on disk (content/blog/)
  • A formatmdx (frontmatter + body) or json (structured data only)
  • Fields — the schema each entry must conform to
  • An optional routePattern — the URL each entry maps to
  • An optional defaultSort for the entry list view
Each entry in the collection becomes one file in the directory.

Creating a Collection

There are three paths. Pick whichever matches your comfort level.
The fastest way. In the AI chat:
Add a "case-studies" collection. Each entry should have a title,
client name, problem, solution, hero image, and a long-form body.
Route them at /case-studies/{slug}.
The AI will:
  1. Read your existing manifest
  2. Propose the collection schema (fields, format, route)
  3. Confirm with you
  4. Save the manifest update
  5. Optionally create a seed entry so the collection isn’t empty
See Add a New Collection for the full step-by-step.

Naming Conventions

A few rules to keep things consistent across collections:
  • id — lowercase, hyphenated, singular when ambiguous. Good: blog, case-study, team-member. Avoid: blog_posts, BlogPosts, posts (too generic).
  • name — Title Case, plural. Good: Blog Posts, Case Studies, Team Members.
  • pathcontent/<id> or content/<plural>. Whichever you pick, be consistent across the manifest.
  • Field name — camelCase. Good: publishedAt, heroImage, seoTitle. Avoid: published_at, Hero-Image.

Format: MDX vs JSON

Pick mdx when entries have a substantial long-form body and you want Markdown features (headings, embeds, components) in that body. Pick json for purely structured data.
Use MDX for…Use JSON for…
Blog postsTestimonials
Case studiesFAQs
Documentation pagesProducts
Long-form essaysTeam members
TutorialsPricing tiers
NewslettersSite settings
MDX collections require exactly one field with isBody: true. JSON collections must not have any. The Collection Manager catches this for you; if you hand-edit the manifest, the validator will too.
You can mix formats in one project — most real sites have one or two MDX collections (blog, docs) and several JSON collections (products, testimonials, FAQs).

References Between Collections

A reference field lets one collection point at another. Common patterns:
  • A blog post references an entry in authors
  • A case-study references one or more products
  • A product references entries in categories
  • A blog post references related entries in blog itself (self-reference is allowed)
In the manifest:
{
  "name": "author",
  "type": "reference",
  "collection": "authors",
  "required": true
}
In the CMS panel, this renders as a searchable picker. On disk, the value is the referenced entry’s slug. See Linking Collections with References for examples of rendering referenced data on the page.

Reusable Patterns

Below are battle-tested schemas for the most common collection types. Use them as starting points and adapt fields to your site.

Blog

{
  "id": "blog",
  "name": "Blog Posts",
  "path": "content/blog",
  "format": "mdx",
  "slugFrom": "filename",
  "fields": [
    { "name": "title", "type": "string", "required": true },
    { "name": "excerpt", "type": "string", "multiline": true, "maxLength": 280 },
    { "name": "publishedAt", "type": "date", "required": true },
    { "name": "status", "type": "select", "options": ["draft", "published"], "default": "draft" },
    { "name": "author", "type": "reference", "collection": "authors", "required": true },
    { "name": "heroImage", "type": "image" },
    { "name": "tags", "type": "array", "items": "string" },
    { "name": "body", "type": "markdown", "isBody": true }
  ],
  "routePattern": "/blog/{slug}",
  "defaultSort": "publishedAt desc"
}

Products (catalog)

{
  "id": "products",
  "name": "Products",
  "path": "content/products",
  "format": "json",
  "slugFrom": "filename",
  "fields": [
    { "name": "name", "type": "string", "required": true },
    { "name": "sku", "type": "string", "required": true },
    { "name": "price", "type": "number", "required": true, "min": 0 },
    { "name": "currency", "type": "select", "options": ["USD", "EUR", "GBP"], "default": "USD" },
    { "name": "inStock", "type": "boolean", "default": true },
    { "name": "images", "type": "array", "items": "image" },
    { "name": "shortDescription", "type": "string", "multiline": true, "maxLength": 280 },
    { "name": "longDescription", "type": "markdown" },
    { "name": "category", "type": "reference", "collection": "categories" }
  ],
  "routePattern": "/products/{slug}",
  "defaultSort": "name asc"
}

Team Members

{
  "id": "team",
  "name": "Team Members",
  "path": "content/team",
  "format": "json",
  "slugFrom": "field:name",
  "fields": [
    { "name": "name", "type": "string", "required": true },
    { "name": "title", "type": "string", "required": true },
    { "name": "bio", "type": "markdown" },
    { "name": "photo", "type": "image", "metadata": true },
    { "name": "email", "type": "string" },
    { "name": "linkedin", "type": "url" },
    { "name": "twitter", "type": "url" },
    { "name": "order", "type": "number", "integer": true, "default": 0 }
  ],
  "defaultSort": "order asc"
}

Testimonials

{
  "id": "testimonials",
  "name": "Testimonials",
  "path": "content/testimonials",
  "format": "json",
  "slugFrom": "filename",
  "fields": [
    { "name": "name", "type": "string", "required": true },
    { "name": "company", "type": "string" },
    { "name": "role", "type": "string" },
    { "name": "quote", "type": "string", "multiline": true, "required": true },
    { "name": "avatar", "type": "image" },
    { "name": "featured", "type": "boolean", "default": false },
    { "name": "rating", "type": "number", "integer": true, "min": 1, "max": 5 }
  ],
  "defaultSort": "featured desc"
}

FAQs

{
  "id": "faqs",
  "name": "FAQs",
  "path": "content/faqs",
  "format": "json",
  "slugFrom": "filename",
  "fields": [
    { "name": "question", "type": "string", "required": true },
    { "name": "answer", "type": "markdown", "required": true },
    { "name": "category", "type": "select", "options": ["billing", "technical", "general"], "default": "general" },
    { "name": "order", "type": "number", "integer": true, "default": 0 }
  ],
  "defaultSort": "category asc"
}

Case Studies

{
  "id": "case-studies",
  "name": "Case Studies",
  "path": "content/case-studies",
  "format": "mdx",
  "slugFrom": "filename",
  "fields": [
    { "name": "title", "type": "string", "required": true },
    { "name": "client", "type": "string", "required": true },
    { "name": "industry", "type": "string" },
    { "name": "summary", "type": "string", "multiline": true, "maxLength": 320 },
    { "name": "heroImage", "type": "image" },
    { "name": "metrics", "type": "array", "items": {
        "type": "object",
        "fields": [
          { "name": "label", "type": "string", "required": true },
          { "name": "value", "type": "string", "required": true }
        ]
    } },
    { "name": "publishedAt", "type": "date" },
    { "name": "body", "type": "markdown", "isBody": true }
  ],
  "routePattern": "/case-studies/{slug}",
  "defaultSort": "publishedAt desc"
}

Site Settings (singleton)

{
  "id": "site-settings",
  "name": "Site Settings",
  "path": "content/site-settings",
  "format": "json",
  "singleton": true,
  "fields": [
    { "name": "siteName", "type": "string", "required": true },
    { "name": "tagline", "type": "string" },
    { "name": "logo", "type": "image" },
    { "name": "primaryColor", "type": "color" },
    { "name": "footerText", "type": "markdown" },
    { "name": "socialLinks", "type": "object", "fields": [
        { "name": "twitter", "type": "url" },
        { "name": "linkedin", "type": "url" },
        { "name": "github", "type": "url" }
    ] }
  ]
}

Editing a Collection’s Schema

You can edit an existing collection’s schema at any time:
  • Add a field — safe. Existing entries get the new field’s default; the form shows it on next open.
  • Remove a field — safe but lossy. The data stays in the file; re-adding a field with the same name restores the editor.
  • Rename a field — invasive. Old data is orphaned. Ask the AI to write a migration if you have many entries.
  • Change a field’s type — risky. Some types are convertible (string to markdown); most aren’t. Test on a draft branch.
Before any large schema change, snapshot the project. The version history drawer captures every manifest edit, so you can always roll back.

Singleton Collections

Set "singleton": true on a collection to enforce a single entry — useful for site settings, the homepage hero, or a single about page. The CMS panel skips the entry list and goes straight to the form.

What’s Next?

Add a Collection

Three paths to add a new collection

Field Types

Every supported field type

References

Link collections with reference fields

Migrate to CMS

Move hardcoded content into CMS collections