Activity Feed
Stableaudit logs
-
MN
Mai shipped the docs preview.
New SaaS blocks are ready for review.
-
AT
An reviewed component states.
Hover, focus, and empty states passed.
-
LP
Linh updated the release checklist.
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.
<div class="w-full max-w-md">
<%= render Senren::ActivityFeedComponent.new(
items: [
{ initials: "MN", title: "Mai shipped the docs preview.", description: "New SaaS blocks are ready for review.", time: "2m ago" },
{ initials: "AT", title: "An reviewed component states.", description: "Hover, focus, and empty states passed.", time: "18m ago" },
{ initials: "LP", title: "Linh updated the release checklist.", time: "1h ago" }
]
) %>
</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.
bin/rails senren:add activity_feed
Create a custom component with the same conventions
Use this when you need an app-specific static component that follows Senren's ViewComponent structure.
bin/rails generate senren:component activity_feed --no-client
Dependencies are resolved by senren:add:
avatar, badge, typography.
At a glance #
Source #
<%= tag.div(**root_attrs("rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-card))] p-4 shadow-sm")) do %>
<% if content? && items.empty? %>
<%= content %>
<% else %>
<ol class="space-y-4">
<% items.each do |item| %>
<li class="relative flex gap-3">
<span class="mt-1 inline-flex h-8 w-8 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--senren-muted))] text-xs font-semibold text-[hsl(var(--senren-muted-foreground))]"><%= item_value(item, :initials) || "!" %></span>
<div class="min-w-0 flex-1">
<p class="text-sm text-[hsl(var(--senren-foreground))]"><%= item_value(item, :title) %></p>
<% if item_value(item, :description).present? %>
<p class="mt-1 text-sm leading-6 text-[hsl(var(--senren-muted-foreground))]"><%= item_value(item, :description) %></p>
<% end %>
<% if item_value(item, :time).present? %>
<time class="mt-1 block text-xs text-[hsl(var(--senren-muted-foreground))]"><%= item_value(item, :time) %></time>
<% end %>
</div>
</li>
<% end %>
</ol>
<% end %>
<% end %>
# frozen_string_literal: true
module Senren
class ActivityFeedComponent < BaseComponent
VARIANTS = { default: '' }.freeze
SIZES = { md: '' }.freeze
def initialize(items: [], class_name: nil, **html)
super(variant: :default, size: :md, class_name: class_name, **html)
@items = Array(items)
end
attr_reader :items
def item_value(item, key)
item.is_a?(Hash) ? (item[key] || item[key.to_s]) : nil
end
end
end
AI agent rules #
Use for
- +audit logs
- +comment threads
- +change history
Avoid
- -transient notifications - use alert or toast
Accessibility #
- Use a list; each entry has a relative time element.