Team Member List
Stableteam management pages
-
MNActive
Mai Nguyen
Owner
-
ATInvited
An Tran
Designer
-
LPActive
Linh Pham
linh@example.com
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/.../team_member_list_example.html.erb
<div class="w-full max-w-md">
<%= render Senren::TeamMemberListComponent.new(
members: [
{ initials: "MN", name: "Mai Nguyen", role: "Owner", status: "Active" },
{ initials: "AT", name: "An Tran", role: "Designer", status: "Invited" },
{ initials: "LP", name: "Linh Pham", email: "linh@example.com", status: "Active" }
]
) %>
</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 team_member_list
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 team_member_list --no-client
Dependencies are resolved by senren:add:
avatar, badge, button, dropdown_menu.
At a glance #
Category
Saas
Class name
Senren::TeamMemberListComponent
Stimulus
—
Variants
default
Depends on
avatar, badge, button, dropdown_menu
Pairs with
invite_member_dialog, search_input
Source #
app/components/senren/team_member_list_component.html.erb
<%= tag.div(**root_attrs("rounded-(--senren-radius) border border-[hsl(var(--senren-border))] bg-[hsl(var(--senren-card))] shadow-sm")) do %>
<% if content? && members.empty? %>
<div class="p-4"><%= content %></div>
<% else %>
<ul class="divide-y divide-[hsl(var(--senren-border))]">
<% members.each do |member| %>
<li class="flex items-center justify-between gap-4 p-4">
<div class="flex min-w-0 items-center gap-3">
<span class="inline-flex h-10 w-10 shrink-0 items-center justify-center rounded-full bg-[hsl(var(--senren-muted))] text-sm font-semibold text-[hsl(var(--senren-muted-foreground))]"><%= initials_for(member) %></span>
<div class="min-w-0">
<p class="truncate text-sm font-medium text-[hsl(var(--senren-foreground))]"><%= member_value(member, :name) %></p>
<p class="truncate text-xs text-[hsl(var(--senren-muted-foreground))]"><%= member_value(member, :role) || member_value(member, :email) %></p>
</div>
</div>
<% if member_value(member, :status).present? %>
<span class="rounded-full bg-[hsl(var(--senren-muted))] px-2 py-1 text-xs font-medium text-[hsl(var(--senren-muted-foreground))]"><%= member_value(member, :status) %></span>
<% end %>
</li>
<% end %>
</ul>
<% end %>
<% end %>
app/components/senren/team_member_list_component.rb
# frozen_string_literal: true
module Senren
class TeamMemberListComponent < BaseComponent
VARIANTS = { default: '' }.freeze
SIZES = { md: '' }.freeze
def initialize(members: [], class_name: nil, **html)
super(variant: :default, size: :md, class_name: class_name, **html)
@members = Array(members)
end
attr_reader :members
def member_value(member, key)
member.is_a?(Hash) ? (member[key] || member[key.to_s]) : nil
end
def initials_for(member)
explicit = member_value(member, :initials)
return explicit if explicit.present?
member_value(member, :name).to_s.split.map { |part| part[0] }.join.first(2).upcase
end
end
end
AI agent rules #
Use for
- +team management pages
Avoid
- -arbitrary user lists - compose with table or card
Accessibility #
- Use semantic list markup.