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.
What is a Content Block?
Section titled “What is a Content Block?”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]
Creating a Block
Section titled “Creating a Block”Basic Block
Section titled “Basic Block”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); }}Block with Rich Text
Section titled “Block with Rich Text”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); }}Block with Images
Section titled “Block with Images”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); }}Block with Repeaters
Section titled “Block with Repeaters”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); }}Registering Blocks
Section titled “Registering Blocks”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, ], ],];Default Blocks
Section titled “Default Blocks”The core package provides these default blocks:
| Block | Fields | Purpose |
|---|---|---|
Heading | content, level | H1-H6 headings |
Paragraph | content | Plain text paragraphs |
CustomRichEditor | content | Rich text with CTA support |
PostsChooser | posts[] | Select blog posts to display |
Block Naming Conventions
Section titled “Block Naming Conventions”- 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.phpField: 'primary_cta' → PHP property: $primaryCtaContentBlockRegistry
Section titled “ContentBlockRegistry”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.
Advanced: Dynamic Block Labels
Section titled “Advanced: Dynamic Block Labels”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'); }); }}Best Practices
Section titled “Best Practices”- 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
SectionorGrid - Use
->collapsible()on Repeaters for large lists - Add
->icon()for visual identification
Troubleshooting
Section titled “Troubleshooting”| Problem | Cause | Solution |
|---|---|---|
| Block not showing | Not registered | Add to config/filament-cms-core.php |
| Block shows wrong fields | Schema cached | Clear cache: php artisan cache:clear |
| Data not saving | Missing field | Check field name matches data key |
| Repeater items lost | Nested arrays | Ensure flat string structure |