Developer Custom Blocks

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

  1. Register the block and reload the builder — the block tile should appear in its category.
  2. Drop it on a page, adjust, save.
  3. Open the page on the frontend and confirm the output (for schema blocks, confirm your theme view renders).
Laravix Documentation · 13.07.2026
Star on GitHub