Skip to content

Content Blocks

Content blocks are the fundamental units of the CMS content builder. They define the form fields that appear when editors create or edit sections.

A content block is a PHP class that implements JFA\FilamentCMSCore\Contracts\ContentBlock and returns a Filament Builder\Block instance:

graph TD
    A[ContentBlock Interface] -->|requires| B[static make(): Block]
    B -->|returns| C["Builder\\Block"]
    C -->|contains| D[Form Fields]
    D -->|stores| E[JSON Data]
namespace App\CMS\Blocks;
use Filament\Forms\Components\Builder\Block;
use Filament\Forms\Components\TextInput;
use Filament\Support\Icons\Heroicon;
use JFA\FilamentCMSCore\Contracts\ContentBlock;
class Cta implements ContentBlock
{
public static function make(): Block
{
return Block::make('cta')
->schema([
TextInput::make('headline')
->required(),
TextInput::make('button_text')
->required(),
TextInput::make('button_url')
->url()
->required(),
])
->label('Call to Action')
->icon(Heroicon::OutlinedSpeakerphone);
}
}
use Filament\Forms\Components\RichEditor;
class Mission implements ContentBlock
{
public static function make(): Block
{
return Block::make('mission')
->schema([
TextInput::make('title')->required(),
RichEditor::make('content')
->required()
->toolbarButtons([
'bold',
'italic',
'link',
'bulletList',
'orderedList',
]),
])
->label('Mission Statement')
->icon(Heroicon::OutlinedFlag);
}
}
use Filament\Forms\Components\FileUpload;
class Hero implements ContentBlock
{
public static function make(): Block
{
return Block::make('hero')
->schema([
TextInput::make('label'),
TextInput::make('headline')->required(),
FileUpload::make('background_image')
->image()
->directory('cms/hero'),
])
->label('Hero Section')
->icon(Heroicon::OutlinedStar);
}
}
use Filament\Forms\Components\Repeater;
use Filament\Forms\Components\Textarea;
class Features implements ContentBlock
{
public static function make(): Block
{
return Block::make('features')
->schema([
TextInput::make('section_title'),
Repeater::make('items')
->schema([
TextInput::make('title')->required(),
Textarea::make('description'),
TextInput::make('icon'),
])
->columns(2)
->collapsible(),
])
->label('Features Grid')
->icon(Heroicon::OutlinedSquares2x2);
}
}

Add your block classes to config/filament-cms-core.php:

return [
'content_blocks' => [
'default' => [
// Package defaults (auto-registered)
],
'custom' => [
App\CMS\Blocks\Hero::class,
App\CMS\Blocks\Cta::class,
App\CMS\Blocks\Mission::class,
App\CMS\Blocks\Features::class,
App\CMS\Blocks\Team::class,
],
],
];

The core package provides these default blocks:

BlockFieldsPurpose
Headingcontent, levelH1-H6 headings
ParagraphcontentPlain text paragraphs
CustomRichEditorcontentRich text with CTA support
PostsChooserposts[]Select blog posts to display
  • Block type: snake_case, matches Block::make('name')
  • Field keys: snake_case in the data array
  • PHP properties: camelCase in Livewire components
  • Section slug: Must match block type and component class name
Block::make('hero') → Section slug: 'hero' → Component: Hero.php
Field: 'primary_cta' → PHP property: $primaryCta

The registry resolves block schemas dynamically:

use JFA\FilamentCMSCore\Forms\Blocks\ContentBlockRegistry;
$registry = app(ContentBlockRegistry::class);
// Get all blocks
$allBlocks = $registry->getAll();
// Get a specific block
$heroBlock = $registry->getByName('hero');
// Returns: Filament\Forms\Components\Builder\Block
// Inspect block schema
$schema = $heroBlock->getChildSchema();

The registry is populated automatically from config/filament-cms-core.php when FilamentCMSCorePlugin boots.

Use the BlockSettings trait to generate dynamic labels based on content:

use JFA\FilamentCMSCore\Forms\Blocks\BlockSettings;
class Hero implements ContentBlock
{
use BlockSettings;
public static function make(): Block
{
return Block::make('hero')
->schema([
TextInput::make('headline')->required(),
])
->label('Hero')
->icon(Heroicon::OutlinedStar)
->afterStateUpdated(function ($state, $set) {
// Update block label in builder UI
$set('builder_label', $state['headline'] ?? 'Hero');
});
}
}
  • Keep blocks focused on a single purpose
  • Use consistent field naming (snake_case)
  • Add ->required() to mandatory fields
  • Use ->helperText() to explain fields to editors
  • Group related fields with Section or Grid
  • Use ->collapsible() on Repeaters for large lists
  • Add ->icon() for visual identification
ProblemCauseSolution
Block not showingNot registeredAdd to config/filament-cms-core.php
Block shows wrong fieldsSchema cachedClear cache: php artisan cache:clear
Data not savingMissing fieldCheck field name matches data key
Repeater items lostNested arraysEnsure flat string structure