Docs / Inlay / Single-file Blocks

Single-file blocks

For a simple block, defining a class and a separate view is more ceremony than the block needs. A single-file block declares everything - handle, metadata, settings - in one Blade file, compiling into the exact same internal BlockDefinition a class + view block resolves to. The registry, the picker, settings resolution, validation, persistence, preview, rendering, publishing, and revisions all treat the two identically; nothing downstream branches on which one produced a given block.

This is not Livewire Volt. It borrows the one-file ergonomic, but a single-file block is an ordinary Blade file rendered through Laravel's real view engine. It may contain nested Livewire tags exactly like a class-based block, but it is not itself a Livewire component and doesn't depend on the Volt package.

{{-- resources/views/inlay/blocks/hero.blade.php --}}
<?php

use App\Enums\ImageSize;
use WeArePixel\Inlay\Schema\Settings;
use WeArePixel\Inlay\SingleFile\Block;

block(
    Block::make('hero')
        ->name('Hero')
        ->category('Headers')
        ->icon('layout-header')
        ->settings(
            Settings::make()
                ->text('heading')->label('Heading')->default('Welcome')
                ->select('imageSize')->label('Image size')->options(ImageSize::class)->default(ImageSize::Large)
        )
);
?>
<section>
    <h1>{{ $heading }}</h1>
    <img class="{{ $imageSize === ImageSize::Small ? 'max-w-md' : 'w-full' }}" src="...">
</section>

The PHP block at the top declares the block; everything after the closing ?> is ordinary Blade markup, rendered with every setting already available as a variable - exactly as $data is available in any Blade view.

Every top-level setting needs a default

A single-file block has no constructor, so there's nothing for the settings declaration to be cross-checked against - it must be authoritative on its own for field type, nullability, default, and enum/value-object type. Concretely, ->default(...) is required on every top-level setting (unlike a class-based block, where the constructor parameter's own default is authoritative and a schema-level .default() is reserved for nested repeater/group fields only). Omitting it fails registration immediately:

Single-file block [hero] setting [heading] has no default(). A single-file block has no constructor to infer one from - every top-level setting must declare its own default via ->default(...).

Everything else about authoring settings - field types, visibleWhen(), enums, LinkValue, repeaters, groups - works identically to the fluent Settings API documented in Fluent settings.

Registration

Register a single file explicitly, e.g. from AppServiceProvider::boot():

use WeArePixel\Inlay\Facades\Inlay;

Inlay::singleFileBlock(resource_path('views/inlay/blocks/hero.blade.php'));

Or register every *.blade.php file directly inside a directory at once:

Inlay::singleFileBlocks(resource_path('views/inlay/blocks'));

Directory discovery uses each file's own declared handle (from Block::make('hero') inside it), not its filename - the filename is purely an organisational convenience for you, not a second source of truth for the handle. A handle already claimed by a class-based block, or by another single-file block, is a registration error rather than a silent override:

Cannot register single-file block [hero] - a class-based block is already registered for that handle.

Referencing a single-file block without a class

A Page Definition's block allow-list needs to name blocks, but a single-file block has no PHP class to put in a list of ::class references. WeArePixel\Inlay\Support\BlockHandle is a small typed value object for exactly this case - a safe, IDE-discoverable way to reference a handle-only block rather than a raw string, which a Page Definition's public API never accepts:

public function allowedBlocks(): array
{
    return [
        Hero::class,
        BlockHandle::make('promo-banner'),
    ];
}

Single-file-specific registration errors

| Situation | Exception message | | --- | --- | | A top-level setting has no ->default() | Single-file block [...] setting [...] has no default(). ... | | The Blade file never calls block(...) | [...] does not declare a single-file block - it must call block(Block::make(...)) in a PHP block at the top of the file. | | Inlay::singleFileBlock() is given a path with no file | No single-file block found at [...]. | | A handle collides with an already-registered block | Cannot register single-file block [...] - a class-based block is already registered for that handle. (or the equivalent for another single-file block) |

See Registering blocks for the errors shared with class-based blocks.

Discovery Call

One call.
We'll both know.

20 minutes to walk through your project, ask the hard questions, and work out honestly if we're the right team for it.

  • One call per day - it gets our full attention
  • Australia's only Laravel Premier Partner
  • Senior engineers only - no juniors on your project
  • Brisbane-based, onshore team

Press Esc to close  ·  B to reopen