K

Core

Empty State

What a surface shows when there is nothing to show: an icon, a heading, a sentence, and the actions that fix it.

On this page

The screen a user meets before they have created anything. EmptyState is a centred icon, a heading, a sentence and — the part that matters — the buttons that get them out of it. Reach for it wherever a list can legitimately be empty, and say what to do next rather than only that there is nothing here.

use NyonCode\WireCore\Foundation\Schema\EmptyState;

How It Works

An empty state is a layout component: no value, no state path. It renders one centred column — a soft grey circle holding the icon, then a heading, then a description, then a row of actions.

Every part is optional and independently omitted. No icon, no circle. Neither a heading nor a description, and the whole text block disappears. No actions, no button row. An EmptyState::make() with nothing set renders an empty centred div, which is why the useful minimum is a heading plus one action.

The description only gets its top margin when a heading is above it, so a description on its own sits where the heading would have been rather than floating below a gap.

Actions are pre-rendered HTML, not action objects. This is the part that surprises people, and it is worth being precise about: the setter is typed Htmlable|string, but the render path casts each entry with (string) $action. An object is therefore only accepted if it has __toString() — and the framework's Action implements Htmlable without being Stringable, so passing one directly throws:

Error: Object of class NyonCode\WireCore\Actions\Action could not be converted to string

Hand it over rendered instead — ->toHtml(), or a Blade string of your own:

->actions([$action->toHtml()])

The table's "no records" state and the standalone <x-wire::empty-state> tag render through the same partial, so whatever you learn here holds in all three.

Basic Usage

EmptyState::make()
->icon('outline:inbox')
->heading('No orders yet')
->description('Orders will appear here as soon as your first customer checks out.')

With Something To Do About It

use NyonCode\WireCore\Actions\Action;
 
EmptyState::make()
->icon('outline:users')
->heading('No team members')
->description('Invite someone to collaborate on this project.')
->actions([
Action::make('invite')->label('Invite a teammate')->toHtml(),
])

Note the ->toHtml(). Buttons wrap onto more rows if you pass several, and the row is centred under the description.

A Closure Instead Of A String

heading() and description() both accept a closure, resolved on read, so the message can depend on what the user is looking at:

EmptyState::make()
->icon('outline:magnifying-glass')
->heading(fn (): string => "Nothing matches \"{$this->search}\"")
->description('Try a shorter search, or clear the filters.')

Extended Example

A dashboard panel that shows either its content or an empty state, inside a real Livewire host:

use Livewire\Component;
use NyonCode\WireCore\Actions\Action;
use NyonCode\WireCore\Foundation\Schema\EmptyState;
use NyonCode\WireCore\Foundation\Schema\Section;
use NyonCode\WireForms\Forms\Form;
use NyonCode\WireForms\Forms\WithForms;
 
class ProjectOverview extends Component
{
use WithForms;
 
public ?array $data = [];
 
public function form(Form $form): Form
{
return $form
->statePath('data')
->schema([
Section::make('members')
->label('Team')
->schema([
EmptyState::make()
->icon('outline:users')
->heading('No team members yet')
->description('Invite someone to collaborate on this project.')
->actions([
Action::make('invite')
->label('Invite a teammate')
->action(fn () => $this->invite())
->toHtml(),
])
->visible(fn (): bool => $this->members->isEmpty()),
]),
]);
}
 
public function invite(): void
{
// ...
}
}

visible() comes from the shared layout surface, so the empty state is absent from the markup entirely once there is something to show.

Standalone Tag

The same partial as a Blade tag, for a surface that has no schema:

<x-wire::empty-state icon="outline:inbox" heading="No records yet" />

A table's own empty state is configured on the table rather than built here — see Tables.

EmptyState API

->icon(string|Icon|null $icon) // shown in a soft circle above the heading
->heading(string|Closure|null $heading) // the primary line
->description(string|Closure|null $description) // the secondary line under it
->actions(array $actions) // pre-rendered HTML strings — see How It Works
->getIcon(): ?string
->getHeading(): ?string
->getDescription(): ?string
->getActionsHtml(): array

Everything else — visible(), hidden(), columnSpan(), schema() — is the shared layout surface. See Common Layout API.

  • Schema — the vocabulary this belongs to, and the shared surface
  • Callout — the other prime component
  • Actions — what goes in the button row
  • Tables — the table's own "no records" state