Block Component Templates: Quick Reference & Checklists
Copy-paste TypeScript + React templates, example data, and practical checklists for building blocks, collections, and…
⚛️ Advanced React Development Guides
Comprehensive React guides covering hooks, performance optimization, and React 19 features. Includes code examples and prompts to boost your workflow.
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-3based 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...
| Need | Template |
|---|---|
| Define a new block type | Block Type Definition |
| Build a block component | Block Component |
| Create example block data | Example Data |
| Define a new collection | Collection Type |
| Add block to BlockRenderer | BlockRenderer Update |
| Add block to page | Page 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
- Type error? Check that you're importing from the right location (@/types/blocks vs @payload-types)
- Component not rendering? Verify BlockRenderer has the correct case for your blockType
- Styling doesn't match? Use browser dev tools to see what's applied; override with more specific Tailwind classes
- Icon not showing? Make sure it's in iconMap and spelled correctly
- TypeScript errors? Hover over the error; usually it's a missing field or wrong type
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…
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…