• Home
BuildWithMatija
Get In Touch
Part 6·Design to Code: Building Block Systems with Payload CMS
  1. Home
  2. Blog
  3. React
  4. Block Component Templates: Quick Reference & Checklists

Block Component Templates: Quick Reference & Checklists

Copy-paste TypeScript + React templates, example data, and practical checklists for building blocks, collections, and…

20th November 2025·Updated on:12th November 2025·MŽMatija Žiberna·
React

⚛️ Advanced React Development Guides

Comprehensive React guides covering hooks, performance optimization, and React 19 features. Includes code examples and prompts to boost your workflow.

No spam. Unsubscribe anytime.

Part 6 of the Design to Code series — Following Icons, Components & Reusable Types

This is a reference page with copy-paste templates and checklists. When you're building a block or collection, come here to grab the template that matches your scenario.

Copy-Paste Templates

Template: Block Type Definition

File: src/types/blocks/[block-name].ts

import type { Media } from '@payload-types';
import type { CTA } from '@/types/blocks';

/**
 * [BlockName] Block
 * [Brief description of what this block displays]
 */
export interface [BlockName]Block {
  // Identification
  id?: string;
  blockType: '[blockName]';
  template: 'default' | 'variant1';

  // Content
  tagline?: string;
  title?: string;
  description?: string;

  // Data
  items?: [Item][];

  // Styling
  bgColor?: 'white' | 'light' | 'dark';
}

export interface [Item] {
  id: string | number;
  title: string;
  description?: string;
  icon?: string;
  image?: Media;
  cta?: CTA;
}

Replace:

  • [BlockName] with your block name (FeaturedIndustries, Testimonials, etc.)
  • [blockName] with camelCase version
  • [Item] with your item type (Industry, Testimonial, etc.)
  • Fields based on your Figma design

Template: Block Component

File: src/components/blocks/[name]/[name]-template-1.tsx

'use client';

import type { [BlockName]Block } from '@/types/blocks';
import { Card, CardContent, CardHeader, CardTitle } from '@/components/ui/card';
import { Button } from '@/components/ui/button';

interface [BlockName]Template1Props {
  data: [BlockName]Block;
}

export function [BlockName]Template1({ data }: [BlockName]Template1Props) {
  const {
    tagline,
    title,
    description,
    items = [],
    bgColor = 'white',
  } = data;

  const bgClass = {
    white: 'bg-white',
    light: 'bg-gray-50',
    dark: 'bg-gray-900',
  }[bgColor];

  return (
    <section className={`py-24 ${bgClass}`}>
      <div className="max-w-7xl mx-auto px-4 sm:px-6 lg:px-8">
        {/* Header */}
        {tagline && (
          <p className="text-sm font-semibold text-blue-600 uppercase tracking-widest mb-2">
            {tagline}
          </p>
        )}
        {title && (
          <h2 className="text-4xl font-bold mb-4">
            {title}
          </h2>
        )}
        {description && (
          <p className="text-xl text-gray-600 mb-12 max-w-2xl">
            {description}
          </p>
        )}

        {/* Items Grid */}
        <div className="grid grid-cols-1 md:grid-cols-3 gap-8">
          {items.map((item) => (
            <Card key={item.id}>
              <CardHeader>
                <CardTitle>{item.title}</CardTitle>
              </CardHeader>
              <CardContent>
                {item.description && <p className="text-gray-600">{item.description}</p>}
              </CardContent>
            </Card>
          ))}
        </div>

        {/* Empty State */}
        {items.length === 0 && (
          <div className="text-center py-12 text-gray-500">
            <p>No items to display</p>
          </div>
        )}
      </div>
    </section>
  );
}

Replace:

  • [BlockName] with your component/type name
  • Grid columns: adjust md:grid-cols-3 based on design
  • Card content: add fields from your type

Template: Example Data

File: src/components/blocks/[name]/[name].example.ts

import type { [BlockName]Block, [Item] } from '@/types/blocks';

const item1: [Item] = {
  id: 1,
  title: 'Item Title',
  description: 'Item description',
  icon: 'Star',
};

const item2: [Item] = {
  id: 2,
  title: 'Another Item',
  description: 'Description',
  icon: 'Heart',
};

export const [blockName]Example: [BlockName]Block = {
  blockType: '[blockName]',
  template: 'default',
  tagline: 'Section Label',
  title: 'Main Heading',
  description: 'Subtitle or description',
  items: [item1, item2],
  bgColor: 'light',
};

Template: Collection Type

File: src/types/collections/[name].ts

import type { Media } from '@payload-types';
import type { CTA } from '@/types/blocks';

/**
 * [Collection Name] Collection
 * [Description of what this entity represents]
 */
export interface [CollectionName] {
  // Core
  id: number;
  title: string;
  slug?: string;

  // Content
  description?: string;
  longDescription?: string;

  // Media
  image?: Media;
  icon?: string;

  // Link
  link?: CTA;

  // Status
  _status?: 'draft' | 'published';
  meta?: {
    title?: string;
    description?: string;
  };

  // Timestamps
  createdAt: string;
  updatedAt: string;
}

// Example item
export const [collectionNameExample]: [CollectionName] = {
  id: 1,
  title: 'Example Item',
  slug: 'example-item',
  description: 'Description of the item',
  image: {
    url: 'https://example.com/image.jpg',
    alt: 'Image alt text',
  },
  icon: 'Star',
  link: {
    label: 'Learn More',
    href: '/example',
  },
  _status: 'published',
  createdAt: new Date().toISOString(),
  updatedAt: new Date().toISOString(),
};

export const [collectionNamePluralData]: [CollectionName][] = [
  [collectionNameExample],
  // Add more examples
];

Template: BlockRenderer Update

File: src/components/block-renderer.tsx

import { [BlockName]Template1 } from '@/components/blocks/[name]';

export function BlockRenderer({ block }: BlockRendererProps) {
  // ... existing blocks ...

  if (block.blockType === '[blockName]') {
    if (block.template === 'default') {
      return <[BlockName]Template1 data={block as any} />;
    }
  }

  console.warn(`Unknown block: ${block.blockType}/${block.template}`);
  return null;
}

Template: Page Data

File: src/app/data.ts

import type { Page } from '@payload-types';
import { [blockName]Example } from '@/components/blocks/[name]/[name].example';

export const homePageData: Page = {
  id: 'home',
  slug: '/',
  title: 'Home',
  layout: [
    {
      blockType: '[blockName]',
      template: 'default',
      title: 'Your Title',
      items: [/* your items */],
    } as [BlockName]Block,
  ],
};

Checklists

Creating a New Block Type

NEW BLOCK CREATION CHECKLIST:

SETUP
□ Created src/types/blocks/[name].ts with type definition
□ Exported from src/types/blocks/index.ts
□ Created interface for items (if needed)

COMPONENT
□ Created src/components/blocks/[name]/[name]-template-1.tsx
□ Imported type from @/types/blocks
□ Used shadcn/ui components (Button, Card, etc.)
□ Customized styling with Tailwind className
□ Handled empty state gracefully

EXAMPLES & DATA
□ Created src/components/blocks/[name]/[name].example.ts
□ Example data matches type exactly
□ Created array of examples (for collections)

INTEGRATION
□ Updated src/components/block-renderer.tsx
□ Added case for blockType
□ Added all template variations
□ Tested component renders without errors

TESTING
□ Added to src/app/data.ts
□ Component renders on page
□ Styling matches Figma design
□ Icons use Lucide (not SVG imports)
□ Buttons use CTA type
□ No TypeScript errors

Creating a New Collection

COLLECTION CREATION CHECKLIST:

SETUP
□ Created src/types/collections/[name].ts
□ Defined interface with all needed fields
□ Used sensible default values for optional fields

EXAMPLES
□ Created example instances of the collection
□ Created array (Plural)Data with multiple examples
□ Example data matches interface exactly
□ Used realistic values for each field

EXPORT
□ Exported type from src/types/collections/index.ts
□ Exported example data from index
□ Verified imports work in blocks

USAGE
□ Used collection type in block type definition
□ Block component correctly displays collection items
□ Page data correctly passes collection array to block
□ No TypeScript errors

Integration Checklist

INTEGRATION CHECKLIST:

TYPES
□ Type imports from correct location (@payload-types or @/types/blocks)
□ All required fields present in example data
□ Optional fields clearly marked with `?:`
□ Fields match Figma design

COMPONENTS
□ Component receives correct type prop
□ Component destructures needed fields
□ Uses shadcn/ui for generic UI
□ Uses Lucide for icons
□ No custom component recreation
□ Proper TypeScript types (no `any` type assertions)

STYLING
□ Colors match Figma (use Tailwind or inline styles)
□ Spacing matches Figma (padding, margins, gaps)
□ Responsive breakpoints work correctly
□ Hover states and transitions feel smooth
□ Dark mode (if needed) works correctly

FUNCTIONALITY
□ Interactive elements work (buttons, links)
□ Icons display correctly
□ Empty states handled gracefully
□ No console errors
□ No warnings

Quick Lookup

I need to...

NeedTemplate
Define a new block typeBlock Type Definition
Build a block componentBlock Component
Create example block dataExample Data
Define a new collectionCollection Type
Add block to BlockRendererBlockRenderer Update
Add block to pagePage Data

Icons to Use

Common icon mappings:

const iconMap = {
  'Zap': Zap,           // Fast, energy
  'Heart': Heart,       // Love, care
  'Star': Star,         // Quality, featured
  'ShoppingBag': ShoppingBag,  // Shopping, retail
  'Briefcase': Briefcase,      // Business, work
  'Mail': Mail,         // Contact, email
  'Phone': Phone,       // Contact, phone
  'MapPin': MapPin,     // Location
  'ArrowRight': ArrowRight,    // Next, forward
  'Check': Check,       // Done, complete
};

Full list at lucide.dev

Styling Utilities

Common Tailwind patterns:

// Section padding
className="py-24"  // Vertical padding
className="px-4 sm:px-6 lg:px-8"  // Responsive horizontal

// Typography
className="text-4xl font-bold"  // Large heading
className="text-xl text-gray-600"  // Subtitle
className="text-sm font-semibold uppercase"  // Label

// Grid layouts
className="grid grid-cols-1 md:grid-cols-3 gap-8"  // 1 col mobile, 3 cols desktop
className="grid grid-cols-2 lg:grid-cols-4 gap-6"  // 2 cols tablet, 4 cols desktop

// Cards & Containers
className="max-w-7xl mx-auto"  // Centered container with max width
className="rounded-lg shadow-lg"  // Border radius + shadow
className="hover:shadow-lg transition-shadow"  // Hover effect

// Backgrounds
className="bg-white"
className="bg-gray-50"
className="bg-gray-900"

When You're Stuck

  1. Type error? Check that you're importing from the right location (@/types/blocks vs @payload-types)
  2. Component not rendering? Verify BlockRenderer has the correct case for your blockType
  3. Styling doesn't match? Use browser dev tools to see what's applied; override with more specific Tailwind classes
  4. Icon not showing? Make sure it's in iconMap and spelled correctly
  5. TypeScript errors? Hover over the error; usually it's a missing field or wrong type
← Previous

3 Proven Practices: Lucide React Icons & shadcn/ui

Enforce UI consistency with Lucide React icons, shadcn/ui components, and a reusable CTA type to cut duplication and…

Next →

Migrate to Payload CMS: Seamless Swap from Dummy Data

How to replace example data with Payload CMS queries, preserve frontend types, and add getPage/getBlock utilities for…

← Back to series
📄View markdown version
0

Frequently Asked Questions

Comments

Leave a Comment

Your email will not be published

Stay updated! Get our weekly digest with the latest learnings on NextJS, React, AI, and web development tips delivered straight to your inbox.

10-2000 characters

• Comments are automatically approved and will appear immediately

• Your name and email will be saved for future comments

• Be respectful and constructive in your feedback

• No spam, self-promotion, or off-topic content

Matija Žiberna
Matija Žiberna
Full-stack developer, co-founder

I'm Matija Žiberna, a self-taught full-stack developer and co-founder passionate about building products, writing clean code, and figuring out how to turn ideas into businesses. I write about web development with Next.js, lessons from entrepreneurship, and the journey of learning by doing. My goal is to provide value through code—whether it's through tools, content, or real-world software.

Table of Contents

  • Copy-Paste Templates
  • Template: Block Type Definition
  • Template: Block Component
  • Template: Example Data
  • Template: Collection Type
  • Template: BlockRenderer Update
  • Template: Page Data
  • Checklists
  • Creating a New Block Type
  • Creating a New Collection
  • Integration Checklist
  • Quick Lookup
  • I need to...
  • Icons to Use
  • Styling Utilities
  • When You're Stuck
On this page:
  • Copy-Paste Templates
  • Checklists
  • Quick Lookup
  • When You're Stuck
Build With Matija Logo

Build with Matija

Matija Žiberna

I turn scattered business knowledge into one usable system. End-to-end system architecture, AI integration, and development.

Quick Links

Payload CMS Websites
  • Bespoke AI Applications
  • Projects
  • How I Work
  • Blog
  • Get in Touch

    Have a project in mind? Let's discuss how we can help your business grow.

    Contact me →
    © 2026BuildWithMatija•Principal-led system architecture•All rights reserved