Collections Overview
Collections are BlocksWeb's powerful system for managing structured, reusable data that can be edited in the CMS and referenced in your components.
What Are Collections?
Collections are developer-defined data structures that represent entities in your application like:
- Blog posts
- Products
- Team members
- Case studies
- Testimonials
- Locations
- Categories
- Any structured data
Think of collections as typed database tables that you define in your code and your content editors can populate through the CMS.
Why Use Collections?
Without Collections
// ❌ Hardcoded data in component
const TeamSection = () => {
const team = [
{ id: 1, name: 'John Doe', role: 'CEO', image: '/john.jpg' },
{ id: 2, name: 'Jane Smith', role: 'CTO', image: '/jane.jpg' }
];
return (
<div>
{team.map(member => (
<div key={member.id}>
<img src={member.image} alt={member.name} />
<h3>{member.name}</h3>
<p>{member.role}</p>
</div>
))}
</div>
);
};Problems:
- Data hardcoded in component
- Content editors can't update team members
- Changes require code deployments
- No reusability across pages
With Collections
// ✅ Dynamic data from collection
const TeamSection: IBlockswebComponent = ({ selectedMembers }) => {
// selectedMembers are full objects, automatically resolved!
return (
<div>
{selectedMembers.map(member => (
<div key={member.id}>
<img src={member.image} alt={member.name} />
<h3>{member.name}</h3>
<p>{member.role}</p>
</div>
))}
</div>
);
};
TeamSection.schema = {
displayName: 'Team Section',
options: [
{
type: 'collection',
name: 'selectedMembers',
label: 'Select Team Members',
collectionName: 'team',
multiple: true
}
]
};Benefits:
- ✅ Content editors manage team members in CMS
- ✅ No code changes needed for content updates
- ✅ Reusable across multiple pages/components
- ✅ Type-safe with TypeScript
- ✅ Automatic data resolution
How Collections Work
The Complete Flow
┌─────────────────────────────────────────────────────────┐
│ 1. DEFINE: Developer defines collection structure │
│ → blocksweb.config.ts │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 2. POPULATE: Content editors add records in CMS │
│ → Products, blog posts, team members, etc. │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 3. SELECT: Editors select records in component options │
│ → Choose which products to display │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 4. STORE: BlocksWeb stores record IDs │
│ → ["product-1", "product-2", "product-3"] │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 5. RESOLVE: BlocksWeb automatically fetches full data │
│ → Queries collection provider │
└─────────────────────────────────────────────────────────┘
↓
┌─────────────────────────────────────────────────────────┐
│ 6. RENDER: Component receives full objects as props │
│ → [{ id: "1", name: "...", price: 99, ... }] │
└─────────────────────────────────────────────────────────┘The Magic: Automatic Data Resolution
Zero Boilerplate Data Fetching
When you use the collection option, BlocksWeb handles everything: fetching, resolving IDs, error handling, and caching. Your component simply receives complete data objects as props. No useEffect, no loading states, no API calls to write!
What you define:
{
type: 'collection',
name: 'product',
collectionName: 'products'
}What BlocksWeb stores:
{ product: "product-123" } // Just the IDWhat your component receives:
{
product: {
id: "product-123",
name: "Awesome Product",
price: 99.99,
description: "The best product ever",
image: "/products/awesome.jpg",
inStock: true
}
}No hooks, no useEffect, no manual fetching!
Collection Types
Single Record Selection
Select one record from a collection:
const ProductShowcase: IBlockswebComponent = ({ featuredProduct }) => {
// featuredProduct is a full object
if (!featuredProduct) return null;
return (
<div>
<h2>{featuredProduct.name}</h2>
<p>${featuredProduct.price}</p>
<img src={featuredProduct.image} alt={featuredProduct.name} />
</div>
);
};
ProductShowcase.schema = {
displayName: 'Product Showcase',
options: [
{
type: 'collection',
name: 'featuredProduct',
label: 'Featured Product',
collectionName: 'products',
multiple: false // Single selection
}
]
};Multiple Record Selection
Select multiple records from a collection:
const ProductGrid: IBlockswebComponent = ({ selectedProducts = [] }) => {
// selectedProducts is an array of full objects
return (
<div className="grid grid-cols-3 gap-4">
{selectedProducts.map(product => (
<div key={product.id}>
<img src={product.image} alt={product.name} />
<h3>{product.name}</h3>
<p>${product.price}</p>
</div>
))}
</div>
);
};
ProductGrid.schema = {
displayName: 'Product Grid',
options: [
{
type: 'collection',
name: 'selectedProducts',
label: 'Products',
collectionName: 'products',
multiple: true // Multiple selection
}
]
};Collection Providers
Collections use providers to fetch and manage data. BlocksWeb includes two built-in providers:
MemoryCollectionProvider
For development and testing - stores data in memory:
// blocksweb.config.ts
import { MemoryCollectionProvider } from '@blocksweb/core';
export const collectionProvider = new MemoryCollectionProvider({
data: {
products: [
{ id: '1', name: 'Product 1', price: 99 },
{ id: '2', name: 'Product 2', price: 149 }
]
}
});Use cases:
- Local development
- Testing
- Prototyping
- Demo applications
CMSCollectionProvider
For production - fetches data from BlocksWeb CMS:
// blocksweb.config.ts
import { CMSCollectionProvider } from '@blocksweb/core';
export const collectionProvider = new CMSCollectionProvider({
apiUrl: process.env.BLOCKSWEB_API_URL,
projectId: process.env.BLOCKSWEB_PROJECT_ID
});Use cases:
- Production applications
- Content managed by CMS
- Multi-user editing
- Real-time content updates
Custom Providers
You can create custom providers for any data source:
import { CollectionProvider } from '@blocksweb/core';
class DatabaseCollectionProvider implements CollectionProvider {
async getRecord(collectionName: string, id: string) {
// Fetch from your database
return await db.query('SELECT * FROM ?? WHERE id = ?', [collectionName, id]);
}
async getRecords(collectionName: string, ids: string[]) {
// Fetch multiple records
return await db.query('SELECT * FROM ?? WHERE id IN (?)', [collectionName, ids]);
}
async queryRecords(collectionName: string, query: any) {
// Custom query logic
// ...
}
}Use cases:
- Integration with existing database
- Custom APIs
- External data sources
- Legacy system integration
When to Use Collections
✅ Use Collections When:
- Reusable content: Products, blog posts, team members
- CMS-managed data: Content editors need to update data
- Structured data: Data has consistent fields
- Multi-page usage: Same data appears on multiple pages
- Dynamic content: Quantity and content changes over time
Examples:
- E-commerce products
- Blog posts and articles
- Team member profiles
- Customer testimonials
- Case studies
- Locations/stores
- FAQs
- Portfolio items
❌ Don't Use Collections When:
- One-off content: Unique page-specific text
- Static data: Never changes (use component options instead)
- Highly relational: Complex joins and relationships (use direct API calls)
- Real-time data: Stock prices, live feeds (use hooks/API calls)
Examples:
- Hero section title/subtitle (use text options)
- Button text (use text options)
- Background colors (use color options)
- Layout toggles (use checkbox options)
Collections vs Component Options
Understanding when to use each:
Component Options (text, image, color, etc.)
Best for: Component-specific, one-off content
// ✅ Good use of options
Hero.schema = {
displayName: 'Hero',
options: [
{ type: 'text', name: 'title', label: 'Hero Title' },
{ type: 'text', name: 'subtitle', label: 'Subtitle' },
{ type: 'image', name: 'background', label: 'Background Image' }
]
};Collections
Best for: Reusable, structured data
// ✅ Good use of collections
BlogList.schema = {
displayName: 'Blog List',
options: [
{
type: 'collection',
name: 'posts',
label: 'Blog Posts',
collectionName: 'blog',
multiple: true
}
]
};Real-World Example
Here's a complete example of a product showcase using collections:
import { IBlockswebComponent } from '@blocksweb/core';
type ProductShowcaseProps = {
title: string;
description: string;
featuredProducts: Array<{
id: string;
name: string;
price: number;
image: string;
description: string;
inStock: boolean;
}>;
};
const ProductShowcase: IBlockswebComponent<ProductShowcaseProps> = ({
title,
description,
featuredProducts = []
}) => {
return (
<section className="py-16">
<div className="container mx-auto">
{/* Component-specific content via options */}
<h2 className="text-4xl font-bold mb-4">{title}</h2>
<p className="text-lg mb-12">{description}</p>
{/* Collection data - automatically resolved */}
<div className="grid grid-cols-3 gap-8">
{featuredProducts.map(product => (
<div key={product.id} className="border rounded-lg p-6">
<img
src={product.image}
alt={product.name}
className="w-full h-48 object-cover mb-4"
/>
<h3 className="text-xl font-bold mb-2">{product.name}</h3>
<p className="text-gray-600 mb-4">{product.description}</p>
<div className="flex items-center justify-between">
<span className="text-2xl font-bold">${product.price}</span>
{product.inStock ? (
<span className="text-green-600">In Stock</span>
) : (
<span className="text-red-600">Out of Stock</span>
)}
</div>
</div>
))}
</div>
</div>
</section>
);
};
ProductShowcase.schema = {
displayName: 'Product Showcase',
category: 'E-commerce',
options: [
{
type: 'text',
name: 'title',
label: 'Section Title',
default: 'Featured Products'
},
{
type: 'text',
name: 'description',
label: 'Description',
default: 'Check out our best-selling products'
},
{
type: 'collection',
name: 'featuredProducts',
label: 'Featured Products',
collectionName: 'products',
multiple: true // Allow selecting multiple products
}
]
};
export default ProductShowcase;Next Steps
Now that you understand collections, learn how to:
- BlocksWeb Configuration - Register collections and components
- Define Collections - Create collection structures
- Collection Providers - Setup data sources
Key Takeaways
✅ Collections are developer-defined data structures ✅ Content editors manage collection records in the CMS ✅ Use the collection option type in component schemas ✅ BlocksWeb automatically resolves IDs to full objects ✅ No manual data fetching code needed in components ✅ Choose the right provider: Memory (dev), CMS (prod), or Custom ✅ Use collections for reusable content, options for one-off content