PetalComponents.Empty (petal_components v4.16.1)

Copy Markdown View Source

The empty state: what a list, table, inbox or search renders when it has nothing to show.

Every app reinvents this anatomy by hand - a piece of media, a title, a short description, one or two actions. <.empty> standardises it so the empty states across a product read as one system:

<.empty
  title="No results found"
  description="No projects match your filters. Try a broader search."
>
  <:actions>
    <.button size="sm" variant="outline" color="gray">Clear filters</.button>
  </:actions>
</.empty>

Every part is optional - a title on its own renders fine. Without an :icon slot a default treatment renders: a muted circle with a dashed ring around a heroicon glyph, marked aria-hidden because it is decorative.

Variants

  • default - centred with generous vertical padding, no border. The page-level empty state.
  • compact - tighter padding and type, sized to sit inside a table frame or a list.
  • card - the floating-panel surface (border + radius), for an empty state that has to hold its own on a busy page.
  • dashed - a dashed border on the gray ramp, the drop-target look. Visual only; it ships no drag-and-drop behaviour.

size (sm/md/lg) scales the media, the type and the spacing of every variant.

A first-run state with actions

<.empty
  variant="dashed"
  title="No projects yet"
  description="Projects hold your environments, deploys and team access."
>
  <:icon><.icon name="hero-folder-plus" class="w-6 h-6 text-gray-400" /></:icon>
  <:actions>
    <.button size="sm">Create your first project</.button>
    <.button size="sm" variant="outline" color="gray">Import from Git</.button>
  </:actions>
  <.a to="/docs/projects" class="text-sm">Learn more about projects</.a>
</.empty>

Inside a data table

<.data_table> takes an :empty slot, so the empty state of a table is the same component as the empty state of a page:

<.data_table id="orders" rows={@rows} state={@state} path={~p"/orders"}>
  <:col :let={row} field={:name}>{row.name}</: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>

Accessibility

The root is a plain <div> - no landmark, no live region. An empty state is static content; announcing that a result set changed is the job of the thing that changed it. The media is aria-hidden="true", and the title, description and actions sit in DOM order so a screen reader gets a coherent read.

Summary

Functions

The empty state: media, title, description, actions, trailing line - every part optional, centred in a column.

Functions

empty(assigns)

The empty state: media, title, description, actions, trailing line - every part optional, centred in a column.

Attributes

  • title (:string) - the headline, e.g. "No results found". Defaults to nil.
  • description (:string) - one or two sentences of supporting text under the title. Defaults to nil.
  • variant (:string) - 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. Defaults to "default". Must be one of "default", "compact", "card", or "dashed".
  • size (:string) - scales the media, the type and the spacing. Defaults to "md". Must be one of "sm", "md", or "lg".
  • class (:any) - CSS class for the outer container. Defaults to nil.
  • Global attributes are accepted.

Slots

  • icon - 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.
  • actions - primary and secondary actions (buttons or links) rendered under the description.
  • inner_block - an optional trailing line, e.g. a "Learn more" link rendered under the actions.