Collections
Define content types with TypeScript schemas and field validation. Collections are the foundation of your content model in SonicJS.
Overview
Collections define your content types using TypeScript schemas. Each collection automatically gets:
- Automatic API endpoints
- Admin UI for content management
- Field validation and constraints
- Version control through git
Creating Collections
Collections are defined in TypeScript and registered in code. The /admin/collections page is read-only — it shows all registered collections but does not allow creating or editing them.
Step 1: Create Collection File
Create a new file in src/collections/ with a .collection.ts extension:
Blog Posts Collection
// src/collections/blog-posts.collection.ts
import type { CollectionConfig } from '@sonicjs-cms/core'
export default {
name: 'blog_posts',
displayName: 'Blog Posts',
description: 'Articles and blog content',
icon: '📝',
schema: {
type: 'object',
properties: {
title: {
type: 'string',
title: 'Title',
required: true,
minLength: 3,
maxLength: 200,
},
slug: {
type: 'slug',
title: 'URL Slug',
required: true,
},
content: {
type: 'richtext',
title: 'Content',
required: true,
},
excerpt: {
type: 'textarea',
title: 'Excerpt',
maxLength: 500,
helpText: 'A short summary of the post',
},
author: {
type: 'reference',
title: 'Author',
collection: 'users',
required: true,
},
publishDate: {
type: 'datetime',
title: 'Publish Date',
required: true,
},
status: {
type: 'select',
title: 'Status',
enum: ['draft', 'published', 'archived'],
enumLabels: ['Draft', 'Published', 'Archived'],
default: 'draft',
},
},
required: ['title', 'slug', 'content', 'author', 'publishDate'],
},
// Display configuration
listFields: ['title', 'author', 'publishDate', 'status'],
searchFields: ['title', 'excerpt', 'content'],
defaultSort: 'createdAt',
defaultSortOrder: 'desc',
// Mark as config-managed (read-only in admin UI)
managed: true,
isActive: true,
} satisfies CollectionConfig
Step 2: Register the Collection
In your application entry point, register your collections:
Register Collections
// src/index.ts
import { createSonicJSApp, registerCollections } from '@sonicjs-cms/core'
import blogPostsCollection from './collections/blog-posts.collection'
// Register custom collections
registerCollections([blogPostsCollection])
// Create and export the app
export default createSonicJSApp({})
Step 3: Restart Dev Server
Collections are registered at bootstrap. Restart the dev server to pick up new collections:
npm run dev
Admin UI
Registered collections appear in the admin UI at /admin/collections (read-only). Content is stored in the shared documents table — no per-collection DB table is created.
Field Types
SonicJS supports 30+ field types for building rich content schemas:
Text Fields
- Name
string- Type
- string
- Description
Single-line text input. Use for titles, names, short text.
- Name
textarea- Type
- string
- Description
Multi-line plain text. Use for descriptions, notes, long text.
- Name
email- Type
- string
- Description
Email with validation. Use for contact emails, author info.
- Name
url- Type
- string
- Description
URL with validation. Use for links, external resources.
- Name
slug- Type
- string
- Description
URL-friendly identifier. Use for page URLs, SEO paths.
- Name
color- Type
- string
- Description
Color picker. Use for theme colors, UI customization.
Rich Content
- Name
richtext- Type
- string
- Description
WYSIWYG HTML editor. Use for blog posts, articles, formatted content.
- Name
markdown- Type
- string
- Description
Markdown editor. Use for documentation, technical content.
- Name
json- Type
- object
- Description
JSON editor. Use for structured data, API responses.
Numbers and Dates
- Name
number- Type
- number
- Description
Numeric input. Use for prices, quantities, ratings.
- Name
date- Type
- string
- Description
Date picker (no time). Use for birthdays, deadlines.
- Name
datetime- Type
- string
- Description
Date and time picker. Use for publish dates, events.
Selections
- Name
select- Type
- string
- Description
Dropdown (single choice). Use for categories, status fields.
- Name
multiselect- Type
- array
- Description
Dropdown (multiple choices). Use for tags, multiple categories.
- Name
radio- Type
- string
- Description
Radio buttons. Use for status, visibility options.
- Name
checkbox- Type
- boolean
- Description
Boolean checkbox. Use for feature toggles, flags.
Media and Files
- Name
media- Type
- string
- Description
Image/media picker. Use for featured images, avatars.
- Name
file- Type
- string
- Description
File upload. Use for PDFs, documents, downloads.
Relationships
- Name
reference- Type
- string
- Description
Reference to another collection. Use for authors, categories.
- Name
array- Type
- array
- Description
Array of items. Can contain any field type.
Content Blocks & Repeatable Fields
There are three main patterns to model structured content in a page: blocks for mixed layouts, arrays for repeatable items, and objects for grouped fields.
When to use each:
- Blocks - Mixed layouts in a single list (page builder sections).
- Arrays - Repeatable items with the same shape (team members, FAQs, links).
- Objects - Grouped fields that belong together (SEO, settings).
Content Blocks
Use an array field with items.blocks to define reusable content blocks (hero, text + image, testimonials, CTAs) with add/remove/reorder controls.
Blocks Field
body: {
type: 'array',
title: 'Content Blocks',
items: {
type: 'object',
discriminator: 'blockType',
blocks: {
text: {
label: 'Text',
properties: {
heading: { type: 'string', title: 'Heading', required: true },
body: { type: 'textarea', title: 'Body text', required: true },
}
},
callToAction: {
label: 'Call To Action',
properties: {
title: { type: 'string', title: 'Heading', required: true },
body: { type: 'textarea', title: 'Body text', required: true },
buttonLabel: { type: 'string', title: 'Button label', required: true },
buttonUrl: { type: 'url', title: 'Button link', required: true },
}
}
}
}
}
Repeatable Lists (Arrays)
Use arrays for repeatable items that share the same structure.
Team Members List
teamMembers: {
type: 'array',
title: 'Team Members',
items: {
type: 'object',
properties: {
name: { type: 'string', title: 'Name', required: true },
role: { type: 'string', title: 'Role' },
bio: { type: 'textarea', title: 'Bio' },
photo: { type: 'media', title: 'Photo' },
},
},
},
Grouped Fields (Objects)
Use objects to group related fields under a single heading.
Grouped SEO Fields
seo: {
type: 'object',
title: 'SEO',
properties: {
title: { type: 'string', title: 'SEO title' },
description: { type: 'textarea', title: 'SEO description' },
},
},
Validation
Built-in Validators
Field Validation
export const productsCollection: CollectionConfig = {
name: 'products',
displayName: 'Products',
schema: {
type: 'object',
properties: {
name: {
type: 'string',
required: true,
minLength: 3,
maxLength: 200,
},
sku: {
type: 'string',
required: true,
pattern: '^[A-Z0-9-]+$', // Only uppercase, numbers, hyphens
},
price: {
type: 'number',
required: true,
minimum: 0,
maximum: 999999,
},
email: {
type: 'email',
required: true,
},
url: {
type: 'url',
required: false,
},
status: {
type: 'select',
enum: ['draft', 'active', 'archived'],
default: 'draft',
},
},
},
managed: true,
isActive: true,
}
Validation Rules
- required - Field must have a value
- minLength / maxLength - String length constraints
- minimum / maximum - Number range constraints
- pattern - Regular expression validation
- enum - Must be one of specified values
- default - Default value if not provided
Examples
E-commerce Product
Product Collection
export const productsCollection: CollectionConfig = {
name: 'products',
displayName: 'Products',
description: 'E-commerce product catalog',
schema: {
type: 'object',
properties: {
name: {
type: 'string',
required: true,
},
sku: {
type: 'string',
required: true,
},
price: {
type: 'number',
required: true,
minimum: 0,
},
description: {
type: 'richtext',
},
images: {
type: 'array',
items: {
type: 'media',
accept: 'image/*',
},
},
category: {
type: 'select',
enum: ['electronics', 'clothing', 'home', 'sports'],
},
inStock: {
type: 'boolean',
default: true,
},
tags: {
type: 'array',
items: { type: 'string' },
},
},
},
icon: '🛍️',
managed: true,
isActive: true,
}
Documentation Page
Docs Collection
export const docsCollection: CollectionConfig = {
name: 'documentation',
displayName: 'Documentation',
schema: {
type: 'object',
properties: {
title: {
type: 'string',
required: true,
},
slug: {
type: 'slug',
required: true,
},
content: {
type: 'markdown',
required: true,
},
category: {
type: 'select',
enum: ['getting-started', 'api', 'guides', 'reference'],
},
order: {
type: 'number',
default: 0,
},
published: {
type: 'boolean',
default: false,
},
},
},
icon: '📚',
managed: true,
isActive: true,
}
Event Management
Events Collection
export const eventsCollection: CollectionConfig = {
name: 'events',
displayName: 'Events',
schema: {
type: 'object',
properties: {
title: {
type: 'string',
required: true,
},
description: {
type: 'richtext',
},
startDate: {
type: 'datetime',
required: true,
},
endDate: {
type: 'datetime',
required: true,
},
location: {
type: 'string',
},
virtualLink: {
type: 'url',
},
capacity: {
type: 'number',
minimum: 1,
},
registration: {
type: 'boolean',
default: false,
},
organizer: {
type: 'reference',
collection: 'users',
},
},
},
icon: '📅',
managed: true,
isActive: true,
}
Using Collections
API Endpoints
Collections automatically get REST API endpoints:
/api/{collection}/api/{collection}/{id}/api/{collection}Auth required/api/{collection}/{id}Auth required/api/{collection}/{id}Auth requiredBest Practices
Collection Design Tips
- Use descriptive field names that reflect your content model - Set appropriate validation rules to maintain data quality - Use references for relationships between collections - Enable search on fields that users will query frequently - Set reasonable defaults for optional fields
Performance Considerations
- Limit the number of fields in
listFieldsto improve list performance - Use appropriate field types (e.g.,selectinstead ofstringfor fixed options) - Index frequently queried fields in your database