---
title: "Complete Guide: Use Existing Payload CMS Block Types"
slug: "payload-cms-use-existing-block-types"
published: "2025-11-16"
updated: "2025-12-25"
validated: "2025-11-12"
categories:
  - "Payload"
tags:
  - "Payload CMS block types"
  - "Payload CMS blocks"
  - "@payload-types"
  - "Hero block Payload"
  - "Services block Payload"
  - "FeaturedProjects block"
  - "shadcn/ui"
  - "Tailwind CSS"
  - "BlockRenderer Payload"
  - "TypeScript Payload types"
  - "dummy data Payload"
  - "CMS integration"
llm-intent: "reference"
audience-level: "beginner"
framework-versions:
  - "payload cms"
  - "@payload-types"
  - "typescript"
  - "react"
  - "shadcn/ui"
status: "stable"
llm-purpose: "Payload CMS block types — use @payload-types to build Hero, Services and CTA blocks with shadcn/ui and Tailwind for fast, type-safe components. Get…"
llm-prereqs:
  - "Access to Payload CMS"
  - "Access to @payload-types"
  - "Access to TypeScript"
  - "Access to React"
  - "Access to shadcn/ui"
llm-outputs:
  - "Completed outcome: Payload CMS block types — use @payload-types to build Hero, Services and CTA blocks with shadcn/ui and Tailwind for fast, type-safe components. Get…"
---

**Summary Triples**
- (Existing Payload block types, should be consumed from, @payload-types (auto-generated types))
- (Implementation flow, consists of, three steps: import the type, build the component, add dummy data)
- (Import example, is, import type { HeroBlock } from '@payload-types';)
- (Component props, should use, the Payload block type (e.g., interface Props { data: HeroBlock }))
- (Type definitions, do not require, creating new custom types when an existing Payload type exists)
- (Local testing, requires, adding dummy data to the page that matches the block type fields)

### {GOAL}
Payload CMS block types — use @payload-types to build Hero, Services and CTA blocks with shadcn/ui and Tailwind for fast, type-safe components. Get…

### {PREREQS}
- Access to Payload CMS
- Access to @payload-types
- Access to TypeScript
- Access to React
- Access to shadcn/ui

### {STEPS}
1. Inspect @payload-types for definitions
2. Create the component file
3. Use shadcn/ui and Tailwind for styling
4. Add renderer routing
5. Add dummy data for testing
6. Test, then swap to Payload data

<!-- llm:goal="Payload CMS block types — use @payload-types to build Hero, Services and CTA blocks with shadcn/ui and Tailwind for fast, type-safe components. Get…" -->
<!-- llm:prereq="Access to Payload CMS" -->
<!-- llm:prereq="Access to @payload-types" -->
<!-- llm:prereq="Access to TypeScript" -->
<!-- llm:prereq="Access to React" -->
<!-- llm:prereq="Access to shadcn/ui" -->
<!-- llm:output="Completed outcome: Payload CMS block types — use @payload-types to build Hero, Services and CTA blocks with shadcn/ui and Tailwind for fast, type-safe components. Get…" -->

# Complete Guide: Use Existing Payload CMS Block Types
> Payload CMS block types — use @payload-types to build Hero, Services and CTA blocks with shadcn/ui and Tailwind for fast, type-safe components. Get…
Matija Žiberna · 2025-11-16

> Part 2 of the [Design to Code](/blog/design-driven-block-systems) series — Following [Design-Driven Development](/blog/design-driven-development-build-types-from-figma)

You have a Payload CMS setup with block types already defined: Hero, Services, FeaturedProjects, CTA, and more. When you encounter one of these existing types, don't create a custom type. Use the one Payload already provides and focus on building the component.

This guide shows you the path for existing Payload blocks.

## When to Use This Guide

Use this guide when:

- Payload CMS already has this block type defined
- You're building a component for Hero, Services, FeaturedProjects, CTA, or other existing types
- You want to use `@payload-types` directly

Skip this guide if you're creating a new block type (use [Creating Custom Block Types](./03-blocks-custom-types.md) instead).

## The Process: Three Steps

Building a component from an existing Payload type is straightforward. Here's the complete flow:

1. Import the type from Payload
2. Build the component
3. Add dummy data to your page

That's it. No type definition needed.

## Step 1: Import from @payload-types

First, check what Payload has defined. Open `@payload-types` (the auto-generated types from your Payload schema):

```typescript
// From @payload-types
import type { HeroBlock } from '@payload-types';
```

This is the source of truth. Payload has already defined all the fields this block type should have. Your job is just to implement a component that uses them.

## Step 2: Build the Component

Create your component file and implement the block using the Payload type.

File: `src/components/blocks/hero/hero-template-1.tsx`

```typescript
'use client';

import type { HeroBlock } from '@payload-types';
import { Card } from '@/components/ui/card';
import { Button } from '@/components/ui/button';

interface HeroTemplate1Props {
  data: HeroBlock;
}

export function HeroTemplate1({ data }: HeroTemplate1Props) {
  const { title, subtitle, backgroundImage, cta } = data;

  return (
    <div
      className="relative h-screen flex items-center justify-center"
      style={{
        backgroundImage: `url(${backgroundImage?.url})`,
        backgroundSize: 'cover',
        backgroundPosition: 'center',
      }}
    >
      {/* Dark overlay */}
      <div className="absolute inset-0 bg-black/50" />

      {/* Content */}
      <div className="relative z-10 text-center text-white max-w-2xl">
        <h1 className="text-6xl font-bold mb-4">{title}</h1>
        {subtitle && <p className="text-xl mb-8">{subtitle}</p>}
        {cta && (
          <Button asChild className="bg-white text-black hover:bg-gray-100">
            <a href={cta.href}>{cta.label}</a>
          </Button>
        )}
      </div>
    </div>
  );
}
```

What's happening here:

The component receives data typed as `HeroBlock` (from Payload). It destructures the fields it needs and renders them. The Payload type tells you exactly what fields are available, so TypeScript will error if you reference a field that doesn't exist. No guessing, no surprises.

Notice we use `shadcn/ui` components (`Button`, `Card`) and `Tailwind` for styling. We customize the appearance to match Figma through className overrides, not by building custom components.

## Step 3: Add to BlockRenderer

Update your block renderer to handle this block type:

File: `src/components/block-renderer.tsx`

```typescript
import { HeroTemplate1 } from '@/components/blocks/hero';

export function BlockRenderer({ block }: BlockRendererProps) {
  // Check if it's a hero block
  if (block.blockType === 'hero') {
    if (block.template === 'template-1') {
      return <HeroTemplate1 data={block} />;
    }
    if (block.template === 'template-2') {
      return <HeroTemplate2 data={block} />;
    }
  }

  // ... other block types ...
}
```

The `BlockRenderer` acts as a router. It checks the `blockType` and `template` fields from Payload and renders the appropriate component. If you need multiple templates for the same block type (template-1, template-2, etc.), add more conditions.

## Step 4: Add Dummy Data to Page

Before Payload feeds you data, you need example data for development. Create it directly in your page data file:

File: `src/app/data.ts`

```typescript
import type { Page } from '@payload-types';

export const homePageData: Page = {
  id: 'home',
  slug: '/',
  title: 'Home',
  layout: [
    {
      blockType: 'hero',
      template: 'template-1',
      title: 'Design. Build. Illuminate.',
      subtitle: 'Custom signage solutions that define how brands are seen.',
      backgroundImage: {
        url: 'https://example.com/hero-bg.jpg',
        alt: 'Hero background',
      },
      cta: {
        label: 'Explore Our Work',
        href: '/portfolio',
      },
    } as HeroBlock,
  ],
};
```

Notice the `as HeroBlock` at the end. This tells TypeScript to treat this object as a `HeroBlock`. TypeScript will error if you're missing required fields or using wrong field names. This is your safety net before Payload is connected.

The data structure matches exactly what Payload would provide. When you switch to Payload later, you just replace this dummy data with real data from the CMS—no component changes needed.

## Using Multiple Templates

Often, a block type has multiple template variations. For example, Hero might have:

- `template-1`: Full-screen image background
- `template-2`: Split layout (text left, image right)
- `template-3`: Text-only minimal design

All three use the same `HeroBlock` type, but they render differently:

```typescript
// Same type, different templates
{
  blockType: 'hero',
  template: 'template-1',
  title: '...',
  // ... fields ...
} as HeroBlock

{
  blockType: 'hero',
  template: 'template-2',
  title: '...',
  // ... same fields ...
} as HeroBlock
```

Create a component for each template:

```typescript
export function HeroTemplate1({ data }: { data: HeroBlock }) { /* ... */ }
export function HeroTemplate2({ data }: { data: HeroBlock }) { /* ... */ }
export function HeroTemplate3({ data }: { data: HeroBlock }) { /* ... */ }
```

Then route to them in BlockRenderer:

```typescript
if (block.blockType === 'hero') {
  if (block.template === 'template-1') return <HeroTemplate1 data={block} />;
  if (block.template === 'template-2') return <HeroTemplate2 data={block} />;
  if (block.template === 'template-3') return <HeroTemplate3 data={block} />;
}
```

All templates share the same type, so they all have the same fields available. Templates just choose which fields to display and how.

## Common Payload Block Types in This Project

Here are the existing Payload block types you'll encounter:

| Block Type | Use Case | Template | Example |
|---|---|---|---|
| `hero` | Page hero section | template-1, template-2 | Full-screen banner with CTA |
| `services` | Service cards | default | Grid of services |
| `featured-projects` | Project showcase | carousel, grid | Projects with images |
| `cta` | Call-to-action section | default | "Get started" section |

Check your Payload schema to see what's defined.

## Customizing to Match Figma

Your component needs to match the Figma design exactly. Use shadcn/ui components as the base and customize with Tailwind:

```typescript
// If Figma shows:
// - Blue background (#0066ff)
// - Rounded corners (12px)
// - Shadow effect

<Card className="bg-blue-600 rounded-xl shadow-lg p-6">
  {/* content */}
</Card>

// Or with inline styles for custom values:
<div
  className="bg-blue-600 rounded-xl shadow-lg p-6"
  style={{
    borderRadius: '12px',
    boxShadow: '0 4px 12px rgba(0, 0, 0, 0.15)',
  }}
>
  {/* content */}
</div>
```

Never create custom components for standard UI elements. Use shadcn/ui, customize with className and style overrides. This keeps your codebase consistent and reduces maintenance burden.

## Key Differences: Existing vs Custom Types

To help you decide which path to take:

| Aspect | Existing Payload Type | Custom Type |
|--------|----------------------|------------|
| Type Definition | Use `@payload-types` | Create in `src/types/blocks/` |
| When to Use | Block already defined in Payload | New block type you're creating |
| Type File Needed | No | Yes |
| Effort | ~30 minutes (just component) | ~1-2 hours (type + component + examples) |
| File Count | Component + BlockRenderer update | Type + Component + Examples + BlockRenderer |

## Testing Your Implementation

Before moving on, make sure:

1. Component imports from `@payload-types`
2. BlockRenderer has the correct conditions
3. Dummy data in page file matches the type exactly
4. Component renders on your page
5. Styling matches Figma design
6. All interactive elements (buttons, links) work

Quick test: add the block to `src/app/data.ts` and visit your page. You should see it rendered immediately.

## Moving to Payload CMS Later

When you connect to real Payload CMS data, your code doesn't change:

```typescript
// Before: example data
import homePageData from '@/app/data';

// After: Payload data
async function HomePage() {
  const homePageData = await getPage('home');
  // ... rest of code unchanged
}
```

Your component, BlockRenderer, and types all work exactly the same. You're just swapping the data source.

## LLM Response Snippet
```json
{
  "goal": "Payload CMS block types — use @payload-types to build Hero, Services and CTA blocks with shadcn/ui and Tailwind for fast, type-safe components. Get…",
  "responses": [
    {
      "question": "What does the article \"Complete Guide: Use Existing Payload CMS Block Types\" cover?",
      "answer": "Payload CMS block types — use @payload-types to build Hero, Services and CTA blocks with shadcn/ui and Tailwind for fast, type-safe components. Get…"
    }
  ]
}
```