Custom Blocks
Every block in the visual builder is a PHP class returning a BlockDefinition. This article shows how to build and register your own block — from a simple styled section to a block with editable fields rendered by your theme.
Prerequisites
- A place to run registration code: your app's
AppServiceProvider, or a plugin's service provider (see Plugin Development). - Basic Blade knowledge for the theme-rendered variant.
Anatomy of a block
A block definition is built fluently:
use Laravix\Cms\Support\BlockDefinition;
class HighlightBlock
{
public static function definition(): BlockDefinition
{
return BlockDefinition::make('highlight')
->label('Highlight')
->icon('fa-star')
->category('laravix::blocks.categories.content')
->canvasHtml(<<<'HTML'
<section style="padding:48px 24px;background:#fef3c7;text-align:center;">
<h2 style="margin:0 0 8px;">Something worth highlighting</h2>
<p style="margin:0;color:#92400e;">Click to edit this text.</p>
</section>
HTML);
}
}
The methods:
| Method | Purpose |
|---|---|
make(string $key) |
Unique block key |
label(string) |
Name in the builder's block panel (plain string or translation key) |
icon(string) / gjsIcon(string) |
Font Awesome icon class for the block tile (e.g. fa-star) |
category(string) |
Panel category — reuse a core one (laravix::blocks.categories.*) or your own label |
canvasHtml(string|Closure) |
The HTML dropped onto the builder canvas. Blocks without it don't appear in the visual builder. |
schema(array|Closure) |
Filament form components describing editable fields (classic blocks) |
contentTypes(array) |
Restrict the block to certain content types — e.g. the core Post List block is ['archive'] only |
defaultData(array) |
Initial data for schema-based blocks |
Registering
Call the registry from a service provider (a plugin's boot(), or your app's AppServiceProvider::boot()):
use Laravix\Cms\Support\BlockRegistry;
BlockRegistry::register(HighlightBlock::definition());
The block now appears in the builder's block panel for every site of the installation.
Builder blocks: canvas HTML is the source of truth
For visual-builder blocks, canvasHtml is both what lands on the canvas and what the editor then freely edits — text, styles, structure. The saved page is the exported HTML, so the block needs no server-side view. Tips from the core blocks:
- Inline styles or the core's
lx-*utility classes keep the block portable. data-gjs-type="button-link"on<a>elements gives them the builder's link-editing controls.- Keep the markup semantic (one
<section>root) so editors can restyle it sanely.
Classic blocks: schema + theme view
Blocks with a schema are edited as structured data and rendered by the theme at request time. The core's hero, text, cards etc. work this way. A schema uses Filament form components:
use Filament\Forms\Components\TextInput;
use Filament\Forms\Components\RichEditor;
->schema(fn () => [
TextInput::make('heading'),
RichEditor::make('content'),
])
The saved content stores ['type' => 'highlight', 'data' => ['heading' => ..., 'content' => ...]] in Content::$blocks, and the theme renders it via views/blocks/highlight.blade.php, which receives the data keys as variables plus $mediaMap:
<section class="py-12">
<h2>{{ $heading ?? '' }}</h2>
<div>{!! $content ?? '' !!}</div>
</section>
For image fields, name the select something_id and resolve it through the media map: $mediaMap[$image_id] ?? null. Laravix\Cms\Support\FieldComponentFactory::mediaSelect('image_id', 'Image') gives you a ready-made media picker.
Checking your work
- Register the block and reload the builder — the block tile should appear in its category.
- Drop it on a page, adjust, save.
- Open the page on the frontend and confirm the output (for schema blocks, confirm your theme view renders).
Related articles
- Custom Code Blocks — no-code HTML/CSS/JS blocks defined in the admin
- Theme Structure
- Plugin Development