Join our Discord Community

Avatar

An image element with a fallback for representing the user.

Loading component...

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

Loading component...

Fallbacks

Loading component...

Groups

Loading component...

API Reference

Avatar

The root avatar container that manages the image loading state.

PropTypeDefaultDescription
classNamestring-Additional CSS classes for styling.
childrenReact.ReactNode-AvatarImage and AvatarFallback components.

AvatarImage

Displays the primary avatar image with automatic fallback handling.

PropTypeDefaultDescription
srcstring-The image source URL.
altstring-Alternative text for accessibility.
classNamestring-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.

PropTypeDefaultDescription
classNamestring-Additional CSS classes for styling.
delayMsnumber600Delay before showing fallback (prevents flash).
childrenReact.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:

  1. Loading: Image is being fetched
  2. Loaded: Image successfully displayed
  3. 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:

SizeClassUse Case
Extra Smallsize-6Compact lists, comments
Smallsize-8Default size, inline text
Mediumsize-10Cards, navigation
Largesize-14Profile headers, modals
Extra Largesize-20Profile 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>
  )
}

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 readers
  • AvatarFallback 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

  1. Images not loading: Check CORS policy and image URLs
  2. Fallback flashing: Adjust delayMs prop on AvatarFallback
  3. Inconsistent sizes: Ensure consistent size classes across components
  4. 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

  1. Alt Text: Descriptive alt attributes improve accessibility and SEO
  2. Image Optimization: Properly sized images improve Core Web Vitals
  3. Structured Data: Consider Person schema markup for user profiles
  4. Lazy Loading: Improves page load performance for image-heavy pages
  5. WebP Format: Use modern image formats when possible for better compression