Senren UI
Components / Empty State

Empty State

Stable

zero-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.