Empty state
The empty state, standardised - media, title, description and actions. Four variants from bare column to dashed drop-target.
No search results
The most common empty state in any product: a search that found nothing. compact keeps it inside the result frame instead of taking over the page, and the secondary action is the way back out - never leave someone staring at a dead end.
No results for “quaterly report”
Check the spelling, or drop a filter to widen the search.
<.empty
variant="compact"
title="No results for “quaterly report”"
description="Check the spelling, or drop a filter to widen the search."
>
<:icon><.icon name="hero-magnifying-glass" class="w-6 h-6 text-gray-400" /></:icon>
<:actions>
<.button size="sm" variant="outline" color="gray" label="Clear filters" />
</:actions>
</.empty>
First run, with a way in
The first-run state earns a card: it is the whole page, so give it the panel surface, a primary CTA that creates the missing thing, and a trailing docs line for people who want to read before they click.
No projects yet
Projects hold your environments, deploys and team access. Create one to get started.
<.empty
variant="card"
size="lg"
title="No projects yet"
description="Projects hold your environments, deploys and team access. Create one to get started."
>
<:icon>
<div class="flex items-center justify-center bg-primary-50 rounded-full size-16 dark:bg-primary-500/10">
<.icon name="hero-folder-plus" class="w-8 h-8 text-primary-600 dark:text-primary-400" />
</div>
</:icon>
<:actions>
<.button size="sm" label="Create your first project" />
<.button size="sm" variant="outline" color="gray" label="Import from Git" />
</:actions>
<.a to="#" class="text-sm">Learn more about projects</.a>
</.empty>
Four treatments
default is the bare centred column, compact tightens it for lists and tables, card wraps it in the panel surface, and dashed is the drop-target look - a visual treatment only, no drag and drop attached.
default
Centred, no border.
compact
Tighter, for lists.
card
The panel surface.
dashed
The drop-target look.
<div class="grid w-full gap-4 sm:grid-cols-2">
<.empty variant="default" size="sm" title="default" description="Centred, no border." />
<.empty variant="compact" size="sm" title="compact" description="Tighter, for lists." />
<.empty variant="card" size="sm" title="card" description="The panel surface." />
<.empty variant="dashed" size="sm" title="dashed" description="The drop-target look." />
</div>
Inside a data table
The data table takes an :empty slot, so a table's empty state is the same component as a page's. compact plus sm sits inside the table frame without blowing it open.
| Name |
|---|
<% state = %State{total: 0} %>
<.data_table id="sx-empty-dt" rows={[]} state={state} path="#">
<:col :let={row} field={:name}>{row.name}</:col>
<:col :let={row} field={:email}>{row.email}</:col>
<:empty>
<.empty
variant="compact"
size="sm"
title="No orders yet"
description="Orders show up here the moment your first customer checks out."
/>
</:empty>
</.data_table>
Properties
| Attribute | Type | Default | Description |
|---|---|---|---|
class
|
any |
nil
|
CSS class for the outer container |
description
|
string |
nil
|
one or two sentences of supporting text under the title |
rest
|
global |
||
size
|
string |
"md"
|
scales the media, the type and the spacing
one of: "sm", "md", "lg"
|
title
|
string |
nil
|
the headline, e.g. "No results found" |
variant
|
string |
"default"
|
default: centred with generous vertical padding; compact: tighter, for table/list empties; card: wrapped in the floating-panel surface; dashed: dashed border, drop-target look
one of: "default", "compact", "card", "dashed"
|
:actions
slot
|
slot |
primary and secondary actions (buttons or links) rendered under the description | |
:icon
slot
|
slot |
custom icon or illustration; without it a default treatment renders - a muted circle with a dashed ring around a heroicon glyph. The media wrapper is always aria-hidden, so keep this slot decorative - any meaningful text belongs in the title or description | |
:inner_block
slot
|
slot |
an optional trailing line, e.g. a "Learn more" link rendered under the actions |