Skip to content

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 ​

typescript
// ❌ 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 ​

typescript
// ✅ 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:

typescript
{
  type: 'collection',
  name: 'product',
  collectionName: 'products'
}

What Blocksweb stores:

typescript
{
  product: "product-123";
} // Just the ID

What your component receives:

typescript
{
  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:

typescript
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:

typescript
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:

typescript
// 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:

typescript
// 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:

typescript
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

typescript
// ✅ 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

typescript
// ✅ 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:

typescript
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:

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