Empty State
Stablezero-result lists
+
No projects yet
Create your first project to start tracking work.
Usage example #
Copy this ERB into a Rails view after installing the component. The snippet below is the same code used by the live preview above.
app/views/.../empty_state_example.html.erb
<div class="w-full max-w-md">
<%= render Senren::EmptyStateComponent.new(
title: "No projects yet",
description: "Create your first project to start tracking work.",
variant: :illustrated
) do |state| %>
<% state.with_actions do %>
<%= render(Senren::ButtonComponent.new(variant: :primary)) { "Create project" } %>
<%= render(Senren::ButtonComponent.new(variant: :ghost)) { "Import CSV" } %>
<% end %>
<% end %>
</div>
Install this component #
Copy the official component into your app
Use this when you want the Senren-maintained implementation copied into app/components/senren.
Terminal
bin/rails senren:add empty_state
Create a custom component with the same conventions
Use this when you need an app-specific static component that follows Senren's ViewComponent structure.
Terminal
bin/rails generate senren:component empty_state --no-client
Dependencies are resolved by senren:add:
button, typography.
At a glance #
Category
Saas
Class name
Senren::EmptyStateComponent
Stimulus
—
Variants
default, illustrated
Depends on
button, typography
Pairs with
card, table
Source #
app/components/senren/empty_state_component.html.erb
<%= tag.div(**root_attrs("rounded-(--senren-radius) border border-dashed p-8 text-center text-[hsl(var(--senren-muted-foreground))]")) do %>
<% if content? && title.blank? && description.blank? %>
<%= content %>
<% else %>
<div class="mx-auto flex h-12 w-12 items-center justify-center rounded-full bg-[hsl(var(--senren-accent))] text-[hsl(var(--senren-accent-foreground))]">
<% if icon? %><%= icon %><% else %><span class="text-lg font-semibold">+</span><% end %>
</div>
<% if title.present? %>
<h2 class="mt-4 font-display text-lg font-semibold text-[hsl(var(--senren-foreground))]"><%= title %></h2>
<% end %>
<% if description.present? %>
<p class="mx-auto mt-2 max-w-sm text-sm leading-6"><%= description %></p>
<% end %>
<% if actions? %>
<div class="mt-5 flex justify-center gap-2"><%= actions %></div>
<% end %>
<% end %>
<% end %>
app/components/senren/empty_state_component.rb
# frozen_string_literal: true
module Senren
class EmptyStateComponent < BaseComponent
renders_one :icon
renders_one :actions
VARIANTS = {
default: 'border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-card))]',
illustrated: 'border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-muted)/0.35)]'
}.freeze
SIZES = { md: '' }.freeze
def initialize(title: nil, description: nil, variant: :default, class_name: nil, **html)
super(variant: variant, size: :md, class_name: class_name, **html)
@title = title
@description = description
end
attr_reader :title, :description
end
end
AI agent rules #
Use for
- +zero-result lists
- +no-data dashboards
Avoid
- -error states - use alert
Accessibility #
- Provide a meaningful heading and a primary action.