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.