Defining Collections
Learn how to define collection structures in BlocksWeb that content editors can populate in the CMS.
Where to Define Collections
Collections are defined in your blocksweb.config.ts file using the CollectionDefinition type:
// blocksweb.config.ts
import { CollectionDefinition } from '@blocksweb/core';
// Define the collection structure
const productCollection: CollectionDefinition = {
name: 'products',
displayName: 'Products',
fields: [
{ name: 'name', type: 'string', label: 'Product Name', required: true },
{ name: 'price', type: 'number', label: 'Price', required: true },
{ name: 'description', type: 'richtext', label: 'Description' },
{ name: 'image', type: 'image', label: 'Product Image' }
]
};
// Register with a provider (see BlocksWeb Configuration guide)
export const collections = [
{
definition: productCollection,
provider: yourProvider // More on this below
}
];Need Help with blocksweb.config.ts?
See the BlocksWeb Configuration guide for complete setup instructions, including how to register collections with providers.
CollectionDefinition Structure
Each collection definition has the following structure:
type CollectionDefinition = {
name: string; // Unique identifier (lowercase, no spaces)
displayName: string; // Human-readable name shown in CMS
description?: string; // Optional description for editors
fields: FieldDefinition[]; // Array of field definitions
icon?: string; // Optional icon for CMS UI
};Example
{
name: 'blog', // Used in code: collectionName: 'blog'
displayName: 'Blog Posts', // Shown in CMS: "Add Blog Posts"
description: 'Manage blog posts and articles',
icon: '📝',
fields: [
// ... field definitions
]
}Field Types
BlocksWeb supports 8 field types for collection fields:
String Field
Text input for short text values.
{
name: 'title',
type: 'string',
label: 'Title',
required: true,
default: '',
maxLength: 100,
placeholder: 'Enter title...'
}Properties:
required?: boolean- Field must have a valuedefault?: string- Default valuemaxLength?: number- Maximum charactersplaceholder?: string- Placeholder text
Use cases: Names, titles, URLs, slugs, short descriptions
Number Field
Numeric input for integers and decimals.
{
name: 'price',
type: 'number',
label: 'Price',
required: true,
default: 0,
min: 0,
max: 10000,
step: 0.01
}Properties:
required?: booleandefault?: numbermin?: number- Minimum valuemax?: number- Maximum valuestep?: number- Increment step
Use cases: Prices, quantities, ratings, counts, percentages
Boolean Field
Checkbox for true/false values.
{
name: 'featured',
type: 'boolean',
label: 'Featured Product',
default: false
}Properties:
default?: boolean
Use cases: Feature flags, visibility toggles, status indicators
Date Field
Date picker for dates and timestamps.
{
name: 'publishedAt',
type: 'date',
label: 'Publish Date',
required: true,
default: () => new Date().toISOString()
}Properties:
required?: booleandefault?: string | (() => string)- ISO date string or function
Use cases: Publish dates, event dates, timestamps, deadlines
Image Field
Image picker integrated with asset manager.
{
name: 'thumbnail',
type: 'image',
label: 'Thumbnail Image',
required: false,
default: '/placeholder.jpg'
}Properties:
required?: booleandefault?: string- Default image URL
Use cases: Thumbnails, featured images, avatars, logos
Richtext Field
Rich text editor for formatted content.
{
name: 'content',
type: 'richtext',
label: 'Article Content',
required: true,
default: '<p>Start writing...</p>'
}Properties:
required?: booleandefault?: string- HTML string
Use cases: Blog content, product descriptions, long-form text
Reference Field
Reference to another collection record.
{
name: 'category',
type: 'reference',
label: 'Category',
collectionName: 'categories', // Which collection to reference
required: false
}Properties:
collectionName: string- Collection to referencerequired?: boolean
Use cases: Categories, authors, related items, parent-child relationships
Asset Field
File upload for documents, videos, etc.
{
name: 'downloadFile',
type: 'asset',
label: 'Downloadable PDF',
accept: '.pdf,.doc,.docx', // Accepted file types
maxSize: 10485760 // Max size in bytes (10MB)
}Properties:
accept?: string- Accepted MIME types or extensionsmaxSize?: number- Maximum file size in bytesrequired?: boolean
Use cases: PDFs, videos, audio files, downloadable resources
Complete Examples
Blog Collection
{
name: 'blog',
displayName: 'Blog Posts',
description: 'Articles and blog posts',
icon: '📝',
fields: [
{
name: 'title',
type: 'string',
label: 'Post Title',
required: true,
maxLength: 100
},
{
name: 'slug',
type: 'string',
label: 'URL Slug',
required: true,
placeholder: 'my-blog-post'
},
{
name: 'author',
type: 'string',
label: 'Author Name',
required: true
},
{
name: 'publishedAt',
type: 'date',
label: 'Publish Date',
required: true
},
{
name: 'featuredImage',
type: 'image',
label: 'Featured Image',
required: false
},
{
name: 'excerpt',
type: 'string',
label: 'Short Description',
maxLength: 200
},
{
name: 'content',
type: 'richtext',
label: 'Post Content',
required: true
},
{
name: 'category',
type: 'reference',
label: 'Category',
collectionName: 'categories',
required: false
},
{
name: 'featured',
type: 'boolean',
label: 'Featured Post',
default: false
},
{
name: 'tags',
type: 'string',
label: 'Tags (comma-separated)',
placeholder: 'react, typescript, web'
}
]
}E-commerce Product Collection
{
name: 'products',
displayName: 'Products',
description: 'E-commerce product catalog',
icon: '🛍️',
fields: [
{
name: 'name',
type: 'string',
label: 'Product Name',
required: true,
maxLength: 100
},
{
name: 'sku',
type: 'string',
label: 'SKU',
required: true,
placeholder: 'PROD-001'
},
{
name: 'price',
type: 'number',
label: 'Price',
required: true,
min: 0,
step: 0.01
},
{
name: 'compareAtPrice',
type: 'number',
label: 'Compare at Price',
min: 0,
step: 0.01
},
{
name: 'description',
type: 'richtext',
label: 'Product Description',
required: true
},
{
name: 'image',
type: 'image',
label: 'Product Image',
required: true
},
{
name: 'inStock',
type: 'boolean',
label: 'In Stock',
default: true
},
{
name: 'stockQuantity',
type: 'number',
label: 'Stock Quantity',
min: 0,
default: 0
},
{
name: 'category',
type: 'reference',
label: 'Category',
collectionName: 'productCategories'
},
{
name: 'featured',
type: 'boolean',
label: 'Featured Product',
default: false
}
]
}Team Members Collection
{
name: 'team',
displayName: 'Team Members',
description: 'Company team members and staff',
icon: '👥',
fields: [
{
name: 'name',
type: 'string',
label: 'Full Name',
required: true
},
{
name: 'role',
type: 'string',
label: 'Job Title',
required: true,
placeholder: 'Software Engineer'
},
{
name: 'email',
type: 'string',
label: 'Email Address',
placeholder: 'name@company.com'
},
{
name: 'photo',
type: 'image',
label: 'Profile Photo',
required: true
},
{
name: 'bio',
type: 'richtext',
label: 'Biography',
default: '<p>Tell us about yourself...</p>'
},
{
name: 'linkedin',
type: 'string',
label: 'LinkedIn URL',
placeholder: 'https://linkedin.com/in/username'
},
{
name: 'twitter',
type: 'string',
label: 'Twitter Handle',
placeholder: '@username'
},
{
name: 'featured',
type: 'boolean',
label: 'Show on About Page',
default: true
},
{
name: 'order',
type: 'number',
label: 'Display Order',
default: 0,
min: 0
}
]
}Testimonials Collection
{
name: 'testimonials',
displayName: 'Testimonials',
description: 'Customer testimonials and reviews',
icon: '⭐',
fields: [
{
name: 'customerName',
type: 'string',
label: 'Customer Name',
required: true
},
{
name: 'company',
type: 'string',
label: 'Company Name',
placeholder: 'Acme Inc.'
},
{
name: 'role',
type: 'string',
label: 'Job Title',
placeholder: 'CEO'
},
{
name: 'photo',
type: 'image',
label: 'Customer Photo'
},
{
name: 'rating',
type: 'number',
label: 'Rating (1-5)',
required: true,
min: 1,
max: 5,
default: 5
},
{
name: 'quote',
type: 'richtext',
label: 'Testimonial Quote',
required: true
},
{
name: 'featured',
type: 'boolean',
label: 'Featured Testimonial',
default: false
},
{
name: 'createdAt',
type: 'date',
label: 'Date Submitted',
default: () => new Date().toISOString()
}
]
}TypeScript Typing
Define TypeScript types that match your collections:
// types/collections.ts
export type BlogPost = {
id: string;
title: string;
slug: string;
author: string;
publishedAt: string;
featuredImage?: string;
excerpt?: string;
content: string;
category?: string;
featured: boolean;
tags?: string;
};
export type Product = {
id: string;
name: string;
sku: string;
price: number;
compareAtPrice?: number;
description: string;
image: string;
inStock: boolean;
stockQuantity: number;
category?: string;
featured: boolean;
};
export type TeamMember = {
id: string;
name: string;
role: string;
email?: string;
photo: string;
bio: string;
linkedin?: string;
twitter?: string;
featured: boolean;
order: number;
};Use these types in your components:
import { IBlockswebComponent } from '@blocksweb/core';
import { Product } from '@/types/collections';
type ProductGridProps = {
products: Product[]; // Type-safe!
};
const ProductGrid: IBlockswebComponent<ProductGridProps> = ({ products }) => {
return (
<div className="grid grid-cols-3 gap-4">
{products.map(product => (
<div key={product.id}>
<h3>{product.name}</h3>
<p>${product.price}</p>
</div>
))}
</div>
);
};Best Practices
1. Use Clear Names
// ✅ Good - Clear and descriptive
{
name: 'blog',
displayName: 'Blog Posts',
fields: [
{ name: 'publishedAt', type: 'date', label: 'Publish Date' }
]
}
// ❌ Bad - Unclear abbreviations
{
name: 'blg',
displayName: 'BP',
fields: [
{ name: 'pubDt', type: 'date', label: 'Pub' }
]
}2. Mark Required Fields
// ✅ Good - Clear requirements
{
name: 'title',
type: 'string',
label: 'Title',
required: true // Editor must fill this
}3. Provide Defaults
// ✅ Good - Sensible defaults
{
name: 'featured',
type: 'boolean',
label: 'Featured',
default: false // Clear default state
}
{
name: 'publishedAt',
type: 'date',
label: 'Publish Date',
default: () => new Date().toISOString() // Auto-set to now
}4. Add Validation
// ✅ Good - Constrained inputs
{
name: 'rating',
type: 'number',
label: 'Rating',
min: 1,
max: 5, // Can't be outside this range
required: true
}
{
name: 'title',
type: 'string',
label: 'Title',
maxLength: 100, // SEO-friendly length
required: true
}5. Use Placeholders
// ✅ Good - Helpful hints
{
name: 'email',
type: 'string',
label: 'Email',
placeholder: 'name@company.com' // Shows expected format
}
{
name: 'slug',
type: 'string',
label: 'URL Slug',
placeholder: 'my-blog-post'
}6. Organize Related Collections
// ✅ Good - Hierarchical structure
export const collections = [
// Products
{ name: 'products', displayName: 'Products', ... },
{ name: 'productCategories', displayName: 'Product Categories', ... },
// Blog
{ name: 'blog', displayName: 'Blog Posts', ... },
{ name: 'blogCategories', displayName: 'Blog Categories', ... },
// Team
{ name: 'team', displayName: 'Team Members', ... },
{ name: 'departments', displayName: 'Departments', ... }
];Complete Configuration Example
Here's a full blocksweb.config.ts with multiple collections:
// blocksweb.config.ts
import { CollectionDefinition } from '@blocksweb/core';
import { MemoryCollectionProvider } from '@blocksweb/core';
export const collections: CollectionDefinition[] = [
{
name: 'blog',
displayName: 'Blog Posts',
description: 'Articles and blog posts',
icon: '📝',
fields: [
{ name: 'title', type: 'string', label: 'Title', required: true },
{ name: 'slug', type: 'string', label: 'URL Slug', required: true },
{ name: 'content', type: 'richtext', label: 'Content', required: true },
{ name: 'publishedAt', type: 'date', label: 'Publish Date', required: true },
{ name: 'featured', type: 'boolean', label: 'Featured', default: false }
]
},
{
name: 'products',
displayName: 'Products',
description: 'Product catalog',
icon: '🛍️',
fields: [
{ name: 'name', type: 'string', label: 'Name', required: true },
{ name: 'price', type: 'number', label: 'Price', required: true, min: 0 },
{ name: 'description', type: 'richtext', label: 'Description' },
{ name: 'image', type: 'image', label: 'Image', required: true },
{ name: 'inStock', type: 'boolean', label: 'In Stock', default: true }
]
},
{
name: 'team',
displayName: 'Team Members',
icon: '👥',
fields: [
{ name: 'name', type: 'string', label: 'Name', required: true },
{ name: 'role', type: 'string', label: 'Role', required: true },
{ name: 'photo', type: 'image', label: 'Photo', required: true },
{ name: 'bio', type: 'richtext', label: 'Bio' }
]
}
];
// For development - use MemoryCollectionProvider
export const collectionProvider = new MemoryCollectionProvider({
data: {
blog: [],
products: [],
team: []
}
});Next Steps
- BlocksWeb Configuration - Register collections in blocksweb.config.ts
- Collection Providers - Setup data sources
- Collections Overview - Understand the system
Common Pitfalls
❌ Wrong: Using Spaces in Names
// ❌ Don't do this
{
name: 'blog posts', // Spaces not allowed!
displayName: 'Blog Posts'
}✅ Correct: Use camelCase or kebab-case
// ✅ Do this
{
name: 'blogPosts', // camelCase
// or
name: 'blog-posts', // kebab-case
displayName: 'Blog Posts'
}❌ Wrong: Missing Required Fields
// ❌ Editor won't know this is required
{
name: 'title',
type: 'string',
label: 'Title'
// Missing required: true
}✅ Correct: Mark Required Fields
// ✅ Clear requirements
{
name: 'title',
type: 'string',
label: 'Title',
required: true
}