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]
E -->|cast by| F[BlockCaster]
F -->|produces| G[BlockCollection]
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') ->label('Headline') ->required(), TextInput::make('button_text') ->label('Button Text') ->required(), TextInput::make('button_url') ->label('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') ->label('Title') ->required(), RichEditor::make('content') ->label('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') ->label('Label'), TextInput::make('headline') ->label('Headline') ->required(), FileUpload::make('background_image') ->label('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') ->label('Section Title'), Repeater::make('items') ->label('Items') ->schema([ TextInput::make('title') ->label('Title') ->required(), Textarea::make('description') ->label('Description'), TextInput::make('icon') ->label('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: $primaryCtaField Labels
Section titled “Field Labels”Always set ->label() on form components. Labels are:
- Auto-extracted by
BlockSchemaExtractorat resolution time - Attached to each
BlockDataviagetFieldLabels() - Used by
renderField()for visual editing tooltips
// Good — label is setTextInput::make('headline') ->label('Headline') ->required();
// Bad — no label (auto-extracted label will be empty)TextInput::make('headline') ->required();ContentBlockRegistry
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.
BlockSchemaExtractor
Section titled “BlockSchemaExtractor”Auto-extracts field labels from your ContentBlock schemas:
use JFA\FilamentCMSCore\Forms\Blocks\BlockSchemaExtractor;
// Get labels for a block type$labels = BlockSchemaExtractor::getLabels('hero');// Returns: ['label' => 'Label', 'headline' => 'Headline', ...]
// Labels are cached per block type// Clear cache in tests:BlockSchemaExtractor::clearCache();This eliminates the need for manual fieldLabels() methods.
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
->label()to all fields for auto-extraction - 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 |
| Empty labels in editor | Missing ->label() | Add ->label() to form components |