For Developers

Block Development

7 min read

This guide covers creating custom blocks for the TallCMS Rich Editor. Blocks are reusable content components that content editors can insert into pages and p...

Block Development Guide

Quick Start

Generate a new block using the Artisan command:

php artisan make:tallcms-block "product showcase"

This creates:

  • Block class: app/Filament/Forms/Components/RichEditor/RichContentCustomBlocks/ProductShowcaseBlock.php
  • Template: resources/views/cms/blocks/product-showcase.blade.php

The block is auto-discovered and immediately available in the Rich Editor.

Block Architecture

File Structure

app/Filament/Forms/Components/RichEditor/RichContentCustomBlocks/
└── MyCustomBlock.php           # Block class with form schema and rendering

resources/views/cms/blocks/
└── my-custom.blade.php         # Blade template for frontend display

Block Class Anatomy

<?php

namespace App\Filament\Forms\Components\RichEditor\RichContentCustomBlocks;

use Filament\Actions\Action;
use Filament\Forms\Components\RichEditor\RichContentCustomBlock;
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Textarea;
use TallCms\Cms\Filament\Blocks\Concerns\HasBlockMetadata;

class ProductShowcaseBlock extends RichContentCustomBlock
{
    use HasBlockMetadata;

    // Required: Unique identifier
    public static function getId(): string
    {
        return 'product_showcase';
    }

    // Required: Display name in block panel
    public static function getLabel(): string
    {
        return 'Product Showcase';
    }

    // Block panel metadata
    public static function getCategory(): string
    {
        return 'content';
    }

    public static function getIcon(): string
    {
        return 'heroicon-o-shopping-bag';
    }

    public static function getDescription(): string
    {
        return 'Showcase a product with image and details';
    }

    public static function getKeywords(): array
    {
        return ['product', 'showcase', 'feature'];
    }

    // Required: Configure the editor modal form
    public static function configureEditorAction(Action $action): Action
    {
        return $action
            ->modalDescription('Configure the product showcase')
            ->schema([
                TextInput::make('title')
                    ->required()
                    ->maxLength(255),
                Textarea::make('description')
                    ->maxLength(500),
            ])->slideOver();
    }

    // Required: Render preview in admin editor
    public static function toPreviewHtml(array $config): string
    {
        return view('cms.blocks.product-showcase', [
            'title' => $config['title'] ?? 'Sample Product',
            'description' => $config['description'] ?? 'Product description',
        ])->render();
    }

    // Required: Render on frontend
    public static function toHtml(array $config, array $data): string
    {
        return view('cms.blocks.product-showcase', [
            'title' => $config['title'] ?? '',
            'description' => $config['description'] ?? '',
        ])->render();
    }
}

Block Metadata (HasBlockMetadata)

MethodReturn TypeDefaultDescription
getCategory()string'content'Category for grouping
getIcon()string'heroicon-o-cube'Heroicon name
getDescription()string''Brief description
getKeywords()array[]Search terms
getSortPriority()int50Sort order (lower = first)

Categories

KeyLabelUse For
contentContentText, CTAs, pricing
mediaMediaImages, galleries
social-proofSocial ProofTestimonials, team
dynamicDynamicPosts, FAQ
formsFormsContact forms
otherOtherUncategorized

Form Schema

Basic Components

use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\Textarea;
use Filament\Forms\Components\Select;
use Filament\Forms\Components\Toggle;
use Filament\Forms\Components\FileUpload;

public static function configureEditorAction(Action $action): Action
{
    return $action
        ->modalWidth('4xl')
        ->schema([
            TextInput::make('title')
                ->required()
                ->maxLength(255),

            Textarea::make('description')
                ->rows(3),

            Select::make('style')
                ->options([
                    'default' => 'Default',
                    'highlight' => 'Highlighted',
                ])
                ->default('default'),

            Toggle::make('show_button')
                ->default(true),

            FileUpload::make('image')
                ->image()
                ->disk(cms_media_disk())
                ->directory('blocks/my-block'),
        ])->slideOver();
}

Repeater for Multiple Items

use Filament\Forms\Components\Repeater;

Repeater::make('items')
    ->schema([
        TextInput::make('title')->required(),
        Textarea::make('description'),
        FileUpload::make('icon')->image(),
    ])
    ->defaultItems(3)
    ->minItems(1)
    ->maxItems(6)
    ->collapsible()
    ->reorderableWithButtons()

Template Development

Basic Template

{{-- resources/views/cms/blocks/product-showcase.blade.php --}}

<section class="py-12 px-4">
    <div class="max-w-4xl mx-auto">
        @if($title)
            <h2 class="text-3xl font-bold mb-4 text-base-content">
                {{ $title }}
            </h2>
        @endif

        @if($description)
            <p class="text-lg text-base-content/80">
                {{ $description }}
            </p>
        @endif
    </div>
</section>

DaisyUI Integration

<a href="{{ $url }}" class="btn btn-primary btn-lg">
    {{ $button_text }}
</a>

<div class="card bg-base-100 shadow-xl">
    <div class="card-body">
        <h2 class="card-title">{{ $title }}</h2>
        <p>{{ $description }}</p>
    </div>
</div>

Image Handling

@if($image)
    <img
        src="{{ Storage::disk(cms_media_disk())->url($image) }}"
        alt="{{ $title }}"
        class="w-full h-auto rounded-lg"
        loading="lazy"
    >
@endif

Advanced Patterns

Preview vs Frontend Rendering

public static function toPreviewHtml(array $config): string
{
    // Provide sample data for preview
    return static::renderBlock(array_merge($config, [
        'title' => $config['title'] ?? 'Sample Title',
    ]), isPreview: true);
}

public static function toHtml(array $config, array $data): string
{
    // Frontend uses actual data
    return static::renderBlock($config, isPreview: false);
}

protected static function renderBlock(array $config, bool $isPreview = false): string
{
    return view('cms.blocks.my-block', [
        'isPreview' => $isPreview,
        'title' => $config['title'] ?? '',
    ])->render();
}

Link Resolution

use TallCms\Cms\Services\BlockLinkResolver;

$buttonUrl = BlockLinkResolver::resolveButtonUrl($config, 'button');

Core Blocks Reference

TallCMS includes 16 built-in blocks:

BlockIDCategory
HeroheroContent
Contentcontent_blockContent
Call to Actioncall_to_actionContent
FeaturesfeaturesContent
PricingpricingContent
DividerdividerContent
Media Galleryimage_galleryMedia
Document Listdocument_listMedia
ParallaxparallaxMedia
TestimonialstestimonialsSocial Proof
TeamteamSocial Proof
LogoslogosSocial Proof
StatsstatsSocial Proof
PostspostsDynamic
FAQfaqDynamic
TimelinetimelineDynamic
Contact Formcontact_formForms

Testing Blocks

Manual Testing Checklist

  1. Admin Preview: Insert block in editor, verify preview
  2. Form Validation: Test required fields, max lengths
  3. Frontend Display: Publish page, verify rendering
  4. Responsive Design: Test mobile, tablet, desktop
  5. Dark Mode: Verify styling in both modes

Common Issues

IssueSolution
Block not appearingVerify class extends RichContentCustomBlock
Preview not updatingCheck toPreviewHtml() returns valid HTML
Styles not applyingRun npm run build, check class names
Images not loadingVerify cms_media_disk() and file path

Next Steps

Choose Theme