{
  "slug": "collection-metadata",
  "title": "Collection Metadata (kind & description)",
  "description": "Tell AI tools what each collection is FOR with `kind` and `description` fields.",
  "category": "reference",
  "order": 27,
  "locale": "en",
  "translationGroup": "f127-collection-metadata-group",
  "helpCardId": null,
  "content": "## Why this matters\n\nWhen you write a `cms.config.ts`, the schema fields tell the CMS **what** a collection holds — but they don't tell anything **what it's for**. Is a `team` collection a page (with its own URL)? A data source for another page's template? Or a form for visitor submissions?\n\nThe inline chat, MCP tools, and any AI agent building or editing your site need this context to make good decisions. Without it, they guess — and often guess wrong.\n\n**Example of what goes wrong without metadata:**\n\n- Chat generates SEO metadata for a `team` collection that has no URL (wasted tokens, never used)\n- Chat adds a \"View\" button that leads to 404 because there's no rendered page\n- Chat remaps `body` → `bio` for a team member, corrupting the field\n- Chat triggers a full site build after updating a single `globals` record (unnecessary)\n\n## Two new fields\n\nAs of F127, `CollectionConfig` accepts two optional fields:\n\n```typescript\ndefineCollection({\n  name: 'team',\n  label: 'Team Members',\n  kind: 'data',                                    // ← NEW\n  description: 'Team members rendered on /about.', // ← NEW\n  fields: [/* ... */],\n});\n```\n\nBoth are **optional and backwards compatible**. Collections without `kind` default to `page` behavior — exactly what the chat does today. But you should populate them on every new collection.\n\n## The five kinds\n\n### `page` (default)\n\nThe collection produces indexable pages with URLs. Each document is a standalone page that appears in the sitemap, needs SEO metadata, and has a preview URL.\n\n**Use for:** blog posts, landing pages, documentation articles, marketing pages.\n\n**Chat behavior:** Full treatment — SEO generation, View pill, `body`/`content` remapping, site rebuild after changes.\n\n```typescript\ndefineCollection({\n  name: 'posts',\n  label: 'Blog Posts',\n  urlPrefix: '/blog',\n  kind: 'page',\n  description: 'Long-form blog articles. Each post has its own URL and appears in the RSS feed.',\n  fields: [/* ... */],\n});\n```\n\n### `snippet`\n\nReusable text fragments that get embedded in other content via the `{{snippet:slug}}` token (see [Snippet Embeds](/docs/snippet-embeds)). They have no standalone URL.\n\n**Use for:** CTAs, disclaimers, author bios, boilerplate text reused across posts.\n\n**Chat behavior:** No SEO, no View pill, still builds (host pages need to re-render with the updated snippet).\n\n```typescript\ndefineCollection({\n  name: 'snippets',\n  label: 'Snippets',\n  kind: 'snippet',\n  description: 'Reusable text fragments embedded in posts via `{{snippet:slug}}`. Used for disclaimers, CTAs, and author bios.',\n  fields: [\n    { name: 'title', type: 'text', required: true },\n    { name: 'content', type: 'richtext', required: true },\n  ],\n});\n```\n\n### `data`\n\nRecords that are rendered on OTHER pages via loops. They're data sources, not pages themselves.\n\n**Use for:** team members, testimonials, FAQ items, products, portfolio projects, job openings.\n\n**Chat behavior:** No SEO, no View pill, no `body`/`content` remapping (uses exact field names from your schema), still builds.\n\n```typescript\ndefineCollection({\n  name: 'team',\n  label: 'Team Members',\n  kind: 'data',\n  description: 'Team members. Rendered in a grid on /about and as bylines on blog posts.',\n  fields: [\n    { name: 'name', type: 'text', required: true },\n    { name: 'role', type: 'text' },\n    { name: 'bio', type: 'textarea' },\n    { name: 'photo', type: 'image' },\n  ],\n});\n```\n\n### `form`\n\nForm submissions — contact forms, lead capture, applications. These are created by end users via frontend forms, never by AI or editors.\n\n**Use for:** `contact-submissions`, `lead-forms`, `applications`, `newsletter-signups`.\n\n**Chat behavior:** READ-ONLY — AI cannot create, update, or delete documents in form collections. Listing and searching is allowed.\n\n```typescript\ndefineCollection({\n  name: 'contact-submissions',\n  label: 'Contact Form',\n  kind: 'form',\n  description: 'Submissions from the /contact form. Created by visitors via frontend form. Reviewed by sales team.',\n  fields: [\n    { name: 'name', type: 'text', required: true },\n    { name: 'email', type: 'text', required: true },\n    { name: 'message', type: 'textarea', required: true },\n    { name: 'submittedAt', type: 'date' },\n  ],\n});\n```\n\n### `global`\n\nSite-wide configuration stored as a single record. No URL, no indexing, just settings.\n\n**Use for:** footer content, social links, analytics IDs, site-wide announcements.\n\n**Chat behavior:** Treated as settings. No SEO, no View pill, single-record mode.\n\n```typescript\ndefineCollection({\n  name: 'globals',\n  label: 'Site Settings',\n  kind: 'global',\n  description: 'Site-wide configuration: footer text, social links, analytics IDs, cookie banner copy. Single record only.',\n  fields: [/* ... */],\n});\n```\n\n> **Note:** The name `globals` is a CMS convention. Never use `settings`, `config`, `admin`, `media`, or `interactives` as collection names — they conflict with built-in admin UI panels.\n\n## Writing good descriptions\n\nA good `description` answers three questions:\n\n1. **What is this?** (\"Team members.\", \"Customer testimonials.\")\n2. **Where does it appear?** (\"Rendered on /about.\", \"Looped on the homepage hero.\")\n3. **What references it?** (\"Referenced by posts.author field.\")\n\n**Good:**\n\n> \"Team members. Referenced by posts.author field. Rendered on /about and as bylines on posts.\"\n\n**Bad:**\n\n> \"Team stuff\" — too vague, tells AI nothing\n\n> \"A collection of team members\" — restates the name, no new information\n\n## Backwards compatibility\n\nBoth fields are optional. Sites written before F127 continue working exactly as before — undefined `kind` defaults to `page` behavior. But there's no reason not to populate them on every collection you add going forward.\n\nThe [Site Config Validator](/docs/site-config-validator) will show a soft warning when a collection is missing `description`. It's advisory, not blocking.\n\n## Related\n\n- [Collections](/docs/collections) — managing documents\n- [Collection Naming](/docs/collection-naming) — reserved names to avoid\n- [Snippet Embeds](/docs/snippet-embeds) — the `snippet` kind in action\n- [AI Builder Guide](/docs/ai-builder-guide) — scaffolding sites with AI",
  "excerpt": "Why this matters\n\nWhen you write a cms.config.ts, the schema fields tell the CMS what a collection holds — but they don't tell anything what it's for. Is a team collection a page (with its own URL)? A data source for another page's template? Or a form for visitor submissions?\n\nThe inline chat, MCP t",
  "seo": {
    "metaTitle": "Collection Metadata (kind & description) — webhouse.app Docs",
    "metaDescription": "Tell AI tools what each CMS collection is for with kind and description fields. Five kinds: page, snippet, data, form, global.",
    "keywords": [
      "webhouse",
      "cms",
      "collections",
      "kind",
      "description",
      "AI",
      "schema"
    ]
  },
  "createdAt": "2026-04-08T00:00:00.000Z",
  "updatedAt": "2026-04-08T00:00:00.000Z"
}