Skip to content

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:

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

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

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

typescript
{
  name: 'title',
  type: 'string',
  label: 'Title',
  required: true,
  default: '',
  maxLength: 100,
  placeholder: 'Enter title...'
}

Properties:

  • required?: boolean - Field must have a value
  • default?: string - Default value
  • maxLength?: number - Maximum characters
  • placeholder?: string - Placeholder text

Use cases: Names, titles, URLs, slugs, short descriptions

Number Field ​

Numeric input for integers and decimals.

typescript
{
  name: 'price',
  type: 'number',
  label: 'Price',
  required: true,
  default: 0,
  min: 0,
  max: 10000,
  step: 0.01
}

Properties:

  • required?: boolean
  • default?: number
  • min?: number - Minimum value
  • max?: number - Maximum value
  • step?: number - Increment step

Use cases: Prices, quantities, ratings, counts, percentages

Boolean Field ​

Checkbox for true/false values.

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

typescript
{
  name: 'publishedAt',
  type: 'date',
  label: 'Publish Date',
  required: true,
  default: () => new Date().toISOString()
}

Properties:

  • required?: boolean
  • default?: string | (() => string) - ISO date string or function

Use cases: Publish dates, event dates, timestamps, deadlines

Image Field ​

Image picker integrated with asset manager.

typescript
{
  name: 'thumbnail',
  type: 'image',
  label: 'Thumbnail Image',
  required: false,
  default: '/placeholder.jpg'
}

Properties:

  • required?: boolean
  • default?: string - Default image URL

Use cases: Thumbnails, featured images, avatars, logos

Richtext Field ​

Rich text editor for formatted content.

typescript
{
  name: 'content',
  type: 'richtext',
  label: 'Article Content',
  required: true,
  default: '<p>Start writing...</p>'
}

Properties:

  • required?: boolean
  • default?: string - HTML string

Use cases: Blog content, product descriptions, long-form text

Reference Field ​

Reference to another collection record.

typescript
{
  name: 'category',
  type: 'reference',
  label: 'Category',
  collectionName: 'categories',  // Which collection to reference
  required: false
}

Properties:

  • collectionName: string - Collection to reference
  • required?: boolean

Use cases: Categories, authors, related items, parent-child relationships

Asset Field ​

File upload for documents, videos, etc.

typescript
{
  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 extensions
  • maxSize?: number - Maximum file size in bytes
  • required?: boolean

Use cases: PDFs, videos, audio files, downloadable resources

Complete Examples ​

Blog Collection ​

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

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

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

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

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

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

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

typescript
// ✅ Good - Clear requirements
{
  name: 'title',
  type: 'string',
  label: 'Title',
  required: true  // Editor must fill this
}

3. Provide Defaults ​

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

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

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

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

Common Pitfalls ​

❌ Wrong: Using Spaces in Names ​

typescript
// ❌ Don't do this
{
  name: 'blog posts',  // Spaces not allowed!
  displayName: 'Blog Posts'
}

✅ Correct: Use camelCase or kebab-case ​

typescript
// ✅ Do this
{
  name: 'blogPosts',   // camelCase
  // or
  name: 'blog-posts', // kebab-case
  displayName: 'Blog Posts'
}

❌ Wrong: Missing Required Fields ​

typescript
// ❌ Editor won't know this is required
{
  name: 'title',
  type: 'string',
  label: 'Title'
  // Missing required: true
}

✅ Correct: Mark Required Fields ​

typescript
// ✅ Clear requirements
{
  name: 'title',
  type: 'string',
  label: 'Title',
  required: true
}