Avatar
An image element with a fallback for representing the user.
Installation
npx shadcn@latest add avatar
Usage
import {
Avatar,
AvatarFallback,
AvatarImage,
} from "@/components/ui/avatar"
export function AvatarDemo() {
return (
<Avatar>
<AvatarImage src="https://github.com/shadcn.png" alt="@shadcn" />
<AvatarFallback>CN</AvatarFallback>
</Avatar>
)
}
Examples
Sizes
Fallbacks
Groups
API Reference
Avatar
The root avatar container that manages the image loading state.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes for styling. |
children | React.ReactNode | - | AvatarImage and AvatarFallback components. |
AvatarImage
Displays the primary avatar image with automatic fallback handling.
Prop | Type | Default | Description |
---|---|---|---|
src | string | - | The image source URL. |
alt | string | - | Alternative text for accessibility. |
className | string | - | Additional CSS classes for styling. |
onLoadingStatusChange | (status: "loading" | "loaded" | "error") => void | - | Callback for image loading state changes. |
AvatarFallback
Displays when the image fails to load or is loading.
Prop | Type | Default | Description |
---|---|---|---|
className | string | - | Additional CSS classes for styling. |
delayMs | number | 600 | Delay before showing fallback (prevents flash). |
children | React.ReactNode | - | Fallback content (text, icon, or component). |
Implementation Details
Architecture
Built on Radix UI's Avatar primitive with enhanced styling:
- Image Loading: Automatic handling of image loading states
- Graceful Degradation: Seamless fallback when images fail
- Accessibility: Proper ARIA attributes and semantic structure
- Performance: Optimized loading with configurable delays
Loading States
The Avatar component handles three distinct loading states:
- Loading: Image is being fetched
- Loaded: Image successfully displayed
- Error: Image failed, fallback shown
<Avatar>
<AvatarImage
src="/image.jpg"
onLoadingStatusChange={(status) => {
console.log('Image status:', status)
}}
/>
<AvatarFallback delayMs={600}>
JD
</AvatarFallback>
</Avatar>
Size Variants
Common size implementations using Tailwind classes:
Size | Class | Use Case |
---|---|---|
Extra Small | size-6 | Compact lists, comments |
Small | size-8 | Default size, inline text |
Medium | size-10 | Cards, navigation |
Large | size-14 | Profile headers, modals |
Extra Large | size-20 | Profile pages, about sections |
Best Practices
Image Optimization
Always provide optimized images for better performance:
<Avatar>
<AvatarImage
src="/avatar-400x400.webp"
alt="User profile picture"
/>
<AvatarFallback>JD</AvatarFallback>
</Avatar>
Fallback Content
Create meaningful fallbacks that enhance user experience:
// Text initials (preferred)
<AvatarFallback>JD</AvatarFallback>
// Icon fallback
<AvatarFallback>
<UserIcon className="size-4" />
</AvatarFallback>
// Custom styled fallback
<AvatarFallback className="bg-gradient-to-br from-purple-400 to-pink-400 text-white">
JS
</AvatarFallback>
Accessibility
Ensure proper accessibility with descriptive alt text:
<Avatar>
<AvatarImage
src="/avatar.jpg"
alt="John Doe, Software Engineer"
/>
<AvatarFallback>JD</AvatarFallback>
</Avatar>
Responsive Sizing
Use responsive classes for different screen sizes:
<Avatar className="size-8 sm:size-10 md:size-12">
<AvatarImage src="/avatar.jpg" alt="User" />
<AvatarFallback>U</AvatarFallback>
</Avatar>
Common Use Cases
User Profile
Display user information with avatar and details:
function UserProfile({ user }) {
return (
<div className="flex items-center gap-4">
<Avatar className="size-16">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback>{user.initials}</AvatarFallback>
</Avatar>
<div>
<h3 className="font-semibold">{user.name}</h3>
<p className="text-muted-foreground">{user.email}</p>
</div>
</div>
)
}
Comment Section
Compact avatars for comments and discussions:
function Comment({ comment }) {
return (
<div className="flex gap-3">
<Avatar className="size-8">
<AvatarImage src={comment.author.avatar} alt={comment.author.name} />
<AvatarFallback>{comment.author.initials}</AvatarFallback>
</Avatar>
<div className="flex-1">
<div className="flex items-center gap-2 mb-1">
<span className="font-medium text-sm">{comment.author.name}</span>
<span className="text-xs text-muted-foreground">{comment.timestamp}</span>
</div>
<p className="text-sm">{comment.content}</p>
</div>
</div>
)
}
Team Directory
Grid layout for team member profiles:
function TeamDirectory({ members }) {
return (
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-6">
{members.map((member) => (
<div key={member.id} className="text-center space-y-2">
<Avatar className="size-16 mx-auto">
<AvatarImage src={member.avatar} alt={member.name} />
<AvatarFallback>{member.initials}</AvatarFallback>
</Avatar>
<div>
<p className="font-medium text-sm">{member.name}</p>
<p className="text-xs text-muted-foreground">{member.role}</p>
</div>
</div>
))}
</div>
)
}
Navigation Menu
User menu with avatar trigger:
function UserMenu({ user }) {
return (
<DropdownMenu>
<DropdownMenuTrigger asChild>
<Button variant="ghost" className="relative h-10 w-10 rounded-full">
<Avatar className="size-10">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback>{user.initials}</AvatarFallback>
</Avatar>
</Button>
</DropdownMenuTrigger>
<DropdownMenuContent className="w-56" align="end" forceMount>
<DropdownMenuLabel>My Account</DropdownMenuLabel>
<DropdownMenuSeparator />
<DropdownMenuItem>Profile</DropdownMenuItem>
<DropdownMenuItem>Settings</DropdownMenuItem>
<DropdownMenuItem>Logout</DropdownMenuItem>
</DropdownMenuContent>
</DropdownMenu>
)
}
Advanced Patterns
Avatar Groups
Overlapping avatar stacks for team representation:
function AvatarGroup({ users, max = 4 }) {
const displayUsers = users.slice(0, max)
const extraCount = users.length - max
return (
<div className="flex -space-x-2">
{displayUsers.map((user, index) => (
<Avatar key={user.id} className="border-2 border-background">
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback>{user.initials}</AvatarFallback>
</Avatar>
))}
{extraCount > 0 && (
<Avatar className="border-2 border-background">
<AvatarFallback>+{extraCount}</AvatarFallback>
</Avatar>
)}
</div>
)
}
Status Indicators
Show user online status with colored rings:
function AvatarWithStatus({ user, status }) {
const statusColors = {
online: 'bg-green-500',
away: 'bg-yellow-500',
offline: 'bg-gray-400'
}
return (
<div className="relative">
<Avatar className={status === 'offline' ? 'grayscale' : ''}>
<AvatarImage src={user.avatar} alt={user.name} />
<AvatarFallback>{user.initials}</AvatarFallback>
</Avatar>
<div className={`absolute -bottom-1 -right-1 size-4 rounded-full border-2 border-background ${statusColors[status]}`} />
</div>
)
}
Custom Shapes
Non-circular avatars for special use cases:
// Square avatar
<Avatar className="rounded-lg">
<AvatarImage src="/avatar.jpg" alt="User" />
<AvatarFallback>U</AvatarFallback>
</Avatar>
// Hexagonal avatar
<Avatar className="clip-path-hexagon">
<AvatarImage src="/avatar.jpg" alt="User" />
<AvatarFallback>U</AvatarFallback>
</Avatar>
// No rounding (rectangular)
<Avatar className="rounded-none">
<AvatarImage src="/avatar.jpg" alt="User" />
<AvatarFallback>U</AvatarFallback>
</Avatar>
Accessibility
Screen Reader Support
The Avatar component provides proper semantic structure:
AvatarImage
includes alt text for screen readersAvatarFallback
provides alternative text when images fail- Proper focus management for interactive avatars
Keyboard Navigation
When used as interactive elements:
<button className="rounded-full focus:outline-none focus:ring-2 focus:ring-primary">
<Avatar>
<AvatarImage src="/avatar.jpg" alt="Open user menu" />
<AvatarFallback>U</AvatarFallback>
</Avatar>
</button>
Color Contrast
Ensure sufficient contrast for fallback text:
// Good contrast
<AvatarFallback className="bg-primary text-primary-foreground">
JD
</AvatarFallback>
// Check contrast ratios for custom colors
<AvatarFallback className="bg-purple-600 text-white">
JD
</AvatarFallback>
Performance Optimization
Image Loading Strategies
// Eager loading for above-the-fold avatars
<AvatarImage src="/avatar.jpg" loading="eager" alt="User" />
// Lazy loading for below-the-fold avatars
<AvatarImage src="/avatar.jpg" loading="lazy" alt="User" />
// Preload critical avatars
useEffect(() => {
const link = document.createElement('link')
link.rel = 'preload'
link.href = '/critical-avatar.jpg'
link.as = 'image'
document.head.appendChild(link)
}, [])
Memory Management
For large lists of avatars, consider virtualization:
import { FixedSizeList as List } from 'react-window'
function VirtualizedAvatarList({ users }) {
const Row = ({ index, style }) => (
<div style={style} className="flex items-center gap-3 px-4">
<Avatar>
<AvatarImage src={users[index].avatar} alt={users[index].name} />
<AvatarFallback>{users[index].initials}</AvatarFallback>
</Avatar>
<span>{users[index].name}</span>
</div>
)
return (
<List height={400} itemCount={users.length} itemSize={60}>
{Row}
</List>
)
}
Troubleshooting
Common Issues
- Images not loading: Check CORS policy and image URLs
- Fallback flashing: Adjust
delayMs
prop on AvatarFallback - Inconsistent sizes: Ensure consistent size classes across components
- Poor performance: Optimize images and consider lazy loading
Debugging
<Avatar>
<AvatarImage
src="/avatar.jpg"
onLoadingStatusChange={(status) => {
console.log('Avatar loading status:', status)
}}
/>
<AvatarFallback delayMs={0}> {/* Remove delay for debugging */}
Debug
</AvatarFallback>
</Avatar>
SEO Considerations
- Alt Text: Descriptive alt attributes improve accessibility and SEO
- Image Optimization: Properly sized images improve Core Web Vitals
- Structured Data: Consider Person schema markup for user profiles
- Lazy Loading: Improves page load performance for image-heavy pages
- WebP Format: Use modern image formats when possible for better compression