Empty State
A placeholder panel for where a list, table or search result would otherwise render nothing.
Key Features
- Four variants —
:dashed(default),:outline,:filled,:plain - Tinted icon tile — any Lucide icon, any RapidRailsUI colour
- Two slots —
mediafor illustrations,actionfor buttons - Announces itself —
role="status" aria-live="polite" - No JavaScript — pure server-rendered HTML
- Starter tier — available on all RRUI licenses
Basic Usage
Both title: and description: are optional. A bare rui_empty_state renders the panel alone.
No users found
Try adjusting your search filters
<%= rui_empty_state(
title: "No users found",
description: "Try adjusting your search filters"
) %>
With an Icon
The icon renders in a soft-tinted round tile. color: accepts any RapidRailsUI colour and tints the tile only.
No projects yet
Create your first project to get started
Nothing to review
You're all caught up
<%= rui_empty_state(
title: "No projects yet",
description: "Create your first project to get started",
icon: :folder_plus,
color: :primary
) %>
With Actions
The action slot accepts many entries. They wrap, and follow the panel's alignment.
No results
Nothing matched that search.
<%= rui_empty_state(
title: "No results",
description: "Nothing matched that search.",
icon: :search_x
) do |empty| %>
<% empty.with_action { rui_button("Clear filters", color: :primary) } %>
<% empty.with_action { rui_button("Browse all", variant: :outline) } %>
<% end %>
Inside a Table
rui_table's with_empty_state slot is the most common home for this. rui_live_search has the same slot.
| Name | |
|---|---|
| No users found Try adjusting your search filters | |
<%= rui_table(collection: @users) do |table| %>
<% table.with_column(key: :name, label: "Name") %>
<% table.with_column(key: :email, label: "Email") %>
<% table.with_empty_state do %>
<%= rui_empty_state(
title: "No users found",
description: "Try adjusting your search filters",
icon: :users,
variant: :plain
) %>
<% end %>
<% end %>
Variants
Pick by what surrounds the panel: :dashed on a bare page, :outline or :plain inside something that already has chrome.
:dashed — Dashed border, transparent fill
Nothing here
:outline — Solid hairline on a solid surface
Nothing here
:filled — No border, tinted fill
Nothing here
:plain — No border, no fill
Nothing here
<%= rui_empty_state(title: "Nothing here", variant: :outline) %>
Sizes
Size drives container padding, icon tile size and the title/description type scale together.
:sm
No invoices
Invoices appear here within 24 hours of a sale.
:base
No invoices
Invoices appear here within 24 hours of a sale.
:lg
No invoices
Invoices appear here within 24 hours of a sale.
<%= rui_empty_state(title: "No invoices", size: :lg) %>
Alignment
Actions follow the alignment, so a start-aligned panel does not centre its buttons.
:center
No activity
Nothing has happened yet.
:start
No activity
Nothing has happened yet.
<%= rui_empty_state(title: "No activity", align: :start) %>
Custom Media
The media slot replaces the icon tile entirely. If you pass both icon: and a media slot, the slot wins — you never get two.
No team members
Invite someone to get started
<%= rui_empty_state(title: "No team members") do |empty| %>
<% empty.with_media do %>
<%= image_tag "illustrations/empty-team.svg", class: "w-40" %>
<% end %>
<% end %>
Accessibility
The container renders role="status" and aria-live="polite". An empty state usually appears because a filter or search changed, and a screen reader user needs to be told the result set is now empty.
Both are overridable if you are placing the panel somewhere static rather than in response to a user action.
<%= rui_empty_state(title: "No data", role: "region", "aria-live": "off") %>