Avatar
A versatile avatar component for displaying user profile images, initials, or placeholder icons with status indicators and social media platform sizes.
Key Features
- Image Display with automatic fallback to initials or icon
- Social Media Sizes - Instagram, X, LinkedIn, Discord, GitHub, etc.
- Status Indicators - Online, offline, busy, away, streaming
- Multiple Shapes - Circle, rounded, square
- Ring Styles - Instagram story ring, semantic colors
- Clickable - Link to user profiles
- Avatar Groups - Overlapping stacks for team displays
- Accessibility - Proper ARIA roles and alt text support
- JavaScript API - Programmatic control with
data-actionattributes
Basic Usage
The avatar component supports images, initials derived from names, or a default user icon fallback.
<%= rui_avatar(src: user.avatar_url, alt: user.name) %>
<%= rui_avatar(name: "John Doe") %>
<%= rui_avatar(initials: "JD", size: :lg) %>
Left to right: Image, Name initials, Custom initials, Fallback icon
Content Types
Avatar automatically falls back through: Image → Initials → Icon
Image
rui_avatar(src: url)
Initials
rui_avatar(name: "Sarah Miller")
Fallback Icon
rui_avatar
Sizes
Standard UI sizes from extra small (24px) to extra large (192px).
xs
sm
md
lg
xl
xl2
xl3
<%= rui_avatar(name: "JD", size: :xs) %>
<%= rui_avatar(name: "JD", size: :sm) %>
<%= rui_avatar(name: "JD", size: :md) %>
<%= rui_avatar(name: "JD", size: :lg) %>
<%= rui_avatar(name: "JD", size: :xl) %>
<%= rui_avatar(name: "JD", size: :xl2) %>
<%= rui_avatar(name: "JD", size: :xl3) %>
Social Media Platform Sizes
Use semantic size names that match exactly what you're building. "Make the right thing the obvious thing."
Why Platform Sizes?
Instead of guessing pixel sizes, use platform-specific names like :ig_profile or :x_tweet. Your code becomes self-documenting!
ig_comment
ig_post
ig_story
ig_profile
<%= rui_avatar(src: url, size: :ig_story, ring: :ig_story) %>
<%= rui_avatar(src: url, size: :ig_profile) %>
<%= rui_avatar(src: url, size: :ig_post) %>
<%= rui_avatar(src: url, size: :ig_comment) %>
X (Twitter)
x_reply
x_tweet
x_profile
Discord
discord_avatar
discord_server
discord_profile
GitHub
github_contrib
github_avatar
github_profile
:linkedin_profile, :linkedin_post, :linkedin_comment
:fb_profile, :fb_post, :fb_story
:slack_avatar, :slack_profile
:twitch_avatar, :twitch_channel
:whatsapp_chat, :whatsapp_profile
:tiktok_profile, :tiktok_comment
Shapes
Three shape options for different visual styles.
Circle (Default)
shape: :circle
Rounded
shape: :rounded
Square
shape: :square
Ring Styles
Add emphasis with ring/border styles. Great for story indicators or status emphasis.
none
white
gray
primary
success
warning
danger
ig_story
ig_live
<%= rui_avatar(src: url, ring: :primary) %>
<%= rui_avatar(src: url, ring: :success) %>
<%= rui_avatar(src: url, ring: :ig_story) %>
<%= rui_avatar(src: url, ring: :ig_live) %>
Status Indicators
Show user availability with status dots that automatically scale with avatar size.
online
offline
busy
away
dnd
streaming
idle
<%= rui_avatar(src: url, status: :online) %>
<%= rui_avatar(src: url, status: :offline) %>
<%= rui_avatar(src: url, status: :busy) %>
<%= rui_avatar(src: url, status: :away) %>
<%= rui_avatar(src: url, status: :dnd) %>
<%= rui_avatar(src: url, status: :streaming) %>
Tip: Status dots have hover titles. Try hovering over them to see the status name!
Clickable Avatars
Make avatars link to user profiles with hover effects.
<%= rui_avatar(src: user.avatar_url, url: user_path(user)) %>
<%= rui_avatar(name: "John Doe", url: "/users/1") %>
Avatar Groups
Create overlapping avatar stacks for team displays and contributor lists.
Stacked Avatars with White Borders
With Count Badge
With Real Avatar Images
Reverse Stacked with Real Images
<!-- Stacked avatars with initials -->
<div class="flex -space-x-4 rtl:space-x-reverse">
<%= rui_avatar(name: "John Doe", size: :md, ring: :white) %>
<%= rui_avatar(name: "Jane Smith", size: :md, ring: :white) %>
<%= rui_avatar(name: "Mike Brown", size: :md, ring: :white) %>
<%= rui_avatar(name: "Sarah Lee", size: :md, ring: :white) %>
</div>
<!-- With real avatar images -->
<div class="flex -space-x-4 rtl:space-x-reverse">
<%= rui_avatar(src: user1.avatar_url, alt: user1.name, size: :md, ring: :white) %>
<%= rui_avatar(src: user2.avatar_url, alt: user2.name, size: :md, ring: :white) %>
<%= rui_avatar(src: user3.avatar_url, alt: user3.name, size: :md, ring: :white) %>
<%= rui_avatar(src: user4.avatar_url, alt: user4.name, size: :md, ring: :white) %>
</div>
<!-- Reverse stacked with real images -->
<div class="flex -space-x-4 rtl:space-x-reverse">
<%= rui_avatar(src: user4.avatar_url, alt: user4.name, size: :md, ring: :white) %>
<%= rui_avatar(src: user3.avatar_url, alt: user3.name, size: :md, ring: :white) %>
<%= rui_avatar(src: user2.avatar_url, alt: user2.name, size: :md, ring: :white) %>
<%= rui_avatar(src: user1.avatar_url, alt: user1.name, size: :md, ring: :white) %>
</div>
<!-- With count badge (z-10 makes badge appear on top) -->
<div class="flex -space-x-4 rtl:space-x-reverse">
<%= rui_avatar(src: user1.avatar_url, alt: user1.name, size: :md, ring: :white) %>
<%= rui_avatar(src: user2.avatar_url, alt: user2.name, size: :md, ring: :white) %>
<%= rui_avatar(src: user3.avatar_url, alt: user3.name, size: :md, ring: :white) %>
<div class="relative z-10 flex items-center justify-center w-10 h-10 text-xs font-medium text-white bg-gray-800 dark:bg-gray-900 border-2 border-white dark:border-gray-900 rounded-full flex-shrink-0">
+99
</div>
</div>
Proper Group Container Classes
flex- Creates flexbox container-space-x-4- Creates negative spacing (16px overlap) for stackingrtl:space-x-reverse- RTL support for right-to-left languagesring: :white- Add white borders for visual separationz-10- (For badges) Ensures count badge appears on top of avatars
Real-World Examples
User Profile Header
<div class="flex items-center gap-4">
<%= rui_avatar(
src: @user.avatar_url,
alt: @user.name,
size: :xl4,
ring: @user.verified? ? :success : :none,
status: @user.online_status
) %>
<div>
<%= rui_text(@user.name, size: :xl, weight: :bold) %>
<%= rui_link("@#{@user.username}", href: user_path(@user), variant: :muted, size: :sm, underline: :none) %>
<%= rui_badge(text: "Verified", color: :success, size: :xs) if @user.verified? %>
</div>
</div>
Comment Thread
<div class="flex gap-3">
<%= rui_avatar(name: comment.user.name, size: :sm, url: user_path(comment.user)) %>
<div class="bg-white dark:bg-zinc-800 rounded-lg p-3 flex-1">
<%= rui_link(comment.user.name, href: user_path(comment.user), size: :sm, weight: :semibold, underline: :none) %>
<%= rui_text(comment.body, size: :sm, color: :muted) %>
<div class="mt-2 flex items-center gap-3">
<%= rui_link("Reply", href: "#", size: :xs, variant: :muted, underline: :hover) %>
<%= rui_link("Like", href: "#", size: :xs, variant: :muted, underline: :hover) %>
<%= rui_text(time_ago_in_words(comment.created_at) + " ago", size: :xs, color: :muted) %>
</div>
</div>
</div>
Chat Interface
Hey! Are you joining the meeting?
2:34 PM
Yes, joining in 5 minutes!
2:35 PM
<div class="flex items-start gap-2">
<%= rui_avatar(name: message.sender.name, size: :discord_avatar, status: message.sender.status) %>
<div class="bg-indigo-100 dark:bg-indigo-900 rounded-lg p-3 max-w-md">
<%= rui_text(message.body, size: :sm) %>
<%= rui_text(message.sent_at.strftime("%l:%M %p"), size: :xs, color: :muted, class: "mt-1") %>
</div>
</div>
Instagram Stories Style
<div class="flex gap-4">
<% @stories.each do |story| %>
<div class="text-center">
<%= rui_avatar(
name: story.user.name,
size: :ig_story,
ring: story.live? ? :ig_live : (story.viewed? ? :gray : :ig_story),
url: story_path(story)
) %>
<% if story.live? %>
<%= rui_badge(text: "LIVE", color: :danger, size: :xs, class: "mt-1") %>
<% else %>
<%= rui_link(story.user.username, href: user_path(story.user), size: :xs, underline: :none, class: "mt-1") %>
<% end %>
</div>
<% end %>
</div>
Team Members Card
Project Team
View allLead Designer
Developer
Product Manager
<div class="flex items-center justify-between">
<div class="flex items-center gap-3">
<%= rui_avatar(name: member.name, size: :sm, status: member.status) %>
<div>
<%= rui_link(member.name, href: user_path(member), size: :sm, weight: :medium, underline: :none) %>
<%= rui_text(member.role, size: :xs, color: :muted) %>
</div>
</div>
<%= rui_badge(text: member.access_level, color: member.admin? ? :primary : :secondary, size: :xs) %>
</div>
Accessibility
The Avatar component follows WAI-ARIA best practices for images and interactive elements.
ARIA Attributes
-
Uses
role="img"for avatar containers -
Automatic
aria-labelgenerated from name or alt text -
Status indicators use
aria-hidden="true"with status in aria-label
Keyboard Navigation
- Clickable avatars are focusable via Tab
- Enter activates linked avatars
- Visible focus ring indicates active element
Semantic HTML
-
Uses
<img>with properaltattribute when image is provided -
Initials fallback uses
<span>with aria-hidden (name in aria-label) -
Clickable avatars wrapped in semantic
<a>element
Screen Reader Support
-
Announces user name from
nameoraltattribute - Status is included in announcement (e.g., "John Doe, online")
- Avatar groups announce count and context
API Reference
rui_avatar
Display user profile images, initials, or placeholder icons with status indicators
| Parameter | Type | Default | Description |
|---|
Content
Image and fallback content options
| Parameter | Type | Default | Description |
|---|---|---|---|
| src | String | — | Image source URL (XSS-protected) |
| alt | String |
name or 'Avatar'
|
Alt text for accessibility |
| name | String | — | Full name (auto-generates up to 2-letter initials) |
| initials | String | — | Custom initials (overrides name-derived) |
Appearance
Visual styling options
| Parameter | Type | Default | Description |
|---|---|---|---|
| size | Symbol |
:md
|
Standard sizes or platform presets
:xs
:sm
:md
:lg
:xl
:xl2
:xl3
:xl4
:xl5
:xl6
|
| shape | Symbol |
:circle
|
Avatar shape
:circle
:rounded
:square
|
| ring | Symbol |
:none
|
Ring/border style around avatar
:none
:white
:gray
:black
:primary
:success
:warning
:danger
:ig_story
:ig_live
|
Status
Online/presence indicator options
| Parameter | Type | Default | Description |
|---|---|---|---|
| status | Symbol | — |
Status indicator dot
:online
:offline
:busy
:away
:dnd
:streaming
:idle
:invisible
|
Behavior
Interaction and grouping options
| Parameter | Type | Default | Description |
|---|---|---|---|
| url | String | — | Makes avatar clickable with this URL |
| group_position | Integer | — | Position in avatar group (1, 2, 3, etc.) - controls overlap and z-index stacking |
Platform Size Presets
Social media platform-specific sizes that match each platform's design system
| Parameter | Type | Default | Description |
|---|---|---|---|
| :ig_* | Symbol | — | Instagram sizes: :ig_story, :ig_profile, :ig_post, :ig_comment |
| :x_* | Symbol | — | X (Twitter) sizes: :x_profile, :x_tweet, :x_reply |
| :linkedin_* | Symbol | — | LinkedIn sizes: :linkedin_profile, :linkedin_post, :linkedin_comment, :linkedin_message |
| :fb_* | Symbol | — | Facebook sizes: :fb_profile, :fb_post, :fb_comment, :fb_story |
| :discord_* | Symbol | — | Discord sizes: :discord_avatar, :discord_profile, :discord_server |
| :slack_* | Symbol | — | Slack sizes: :slack_avatar, :slack_profile, :slack_sidebar |
| :github_* | Symbol | — | GitHub sizes: :github_avatar, :github_profile, :github_contrib |
| :yt_* | Symbol | — | YouTube sizes: :yt_channel, :yt_comment, :yt_thumbnail |
| :tiktok_* | Symbol | — | TikTok sizes: :tiktok_profile, :tiktok_comment, :tiktok_for_you |
| :twitch_* | Symbol | — | Twitch sizes: :twitch_avatar, :twitch_channel, :twitch_thumbnail |
| :whatsapp_* | Symbol | — | WhatsApp sizes: :whatsapp_chat, :whatsapp_profile, :whatsapp_group |