Installation
pnpm dlx shadcn@latest add https://rdf.dev.360online.com/r/avatar.jsonInstalls to @/components/ui/avatar.
Manual installation
Install the dependencies:
pnpm add @base-ui/react@^1.8.0Copy the source into your project:
'use client'
import { Avatar as AvatarPrimitive } from '@base-ui/react/avatar'
import type * as React from 'react'
import { cn } from '@/lib/utils'
function Avatar({
className,
size = 'default',
...props
}: AvatarPrimitive.Root.Props & {
size?: 'default' | 'sm' | 'lg'
}) {
return (
<AvatarPrimitive.Root
data-slot="avatar"
data-size={size}
// The outline only frames photos; the initials fallback draws its own border
className={cn(
'group/avatar relative flex size-8 shrink-0 rounded-full select-none data-[size=lg]:size-10 data-[size=sm]:size-6 has-data-[slot=avatar-image]:after:pointer-events-none has-data-[slot=avatar-image]:after:absolute has-data-[slot=avatar-image]:after:inset-0 has-data-[slot=avatar-image]:after:rounded-full has-data-[slot=avatar-image]:after:border has-data-[slot=avatar-image]:after:border-border has-data-[slot=avatar-image]:after:mix-blend-darken dark:has-data-[slot=avatar-image]:after:mix-blend-lighten',
className,
)}
{...props}
/>
)
}
function AvatarImage({ className, ...props }: AvatarPrimitive.Image.Props) {
return (
<AvatarPrimitive.Image
data-slot="avatar-image"
className={cn('aspect-square size-full rounded-full object-cover', className)}
{...props}
/>
)
}
function AvatarFallback({ className, ...props }: AvatarPrimitive.Fallback.Props) {
return (
<AvatarPrimitive.Fallback
data-slot="avatar-fallback"
className={cn(
'flex size-full items-center justify-center rounded-full border border-primary bg-secondary text-sm text-primary group-data-[size=sm]/avatar:text-xs',
className,
)}
{...props}
/>
)
}
function AvatarBadge({ className, ...props }: React.ComponentProps<'span'>) {
return (
<span
data-slot="avatar-badge"
className={cn(
'absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none',
'group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden',
'group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2',
'group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2',
className,
)}
{...props}
/>
)
}
function AvatarGroup({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="avatar-group"
className={cn(
'group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background',
className,
)}
{...props}
/>
)
}
function AvatarGroupCount({ className, ...props }: React.ComponentProps<'div'>) {
return (
<div
data-slot="avatar-group-count"
className={cn(
'relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3',
className,
)}
{...props}
/>
)
}
export { Avatar, AvatarImage, AvatarFallback, AvatarGroup, AvatarGroupCount, AvatarBadge }A versatile avatar component for displaying user images with graceful fallbacks. Built on top of Base UI Avatar, it provides a fully accessible and customizable avatar implementation.
Features
- Image-based avatars with automatic fallback while loading or on error
- Three sizes via
size:sm(24px),default(32px),lg(40px) AvatarBadgefor status dots or small icons in the bottom-right cornerAvatarGroupfor overlapping stacks, withAvatarGroupCountfor overflow- Group count adapts to the size of the avatars in the group
- Custom styling via
className
Usage
import {
Avatar,
AvatarBadge,
AvatarFallback,
AvatarGroup,
AvatarGroupCount,
AvatarImage,
} from '@/components/ui/avatar'
// Basic usage
export function OfficerAvatar() {
return (
<Avatar>
<AvatarImage src="/avatars/anna-lindqvist.jpg" alt="Anna Lindqvist" />
<AvatarFallback>AL</AvatarFallback>
</Avatar>
)
}
// Large with status badge
export function StatusAvatar() {
return (
<Avatar size="lg">
<AvatarImage src="/avatars/ingrid-hansen.jpg" alt="Ingrid Hansen" />
<AvatarFallback>IH</AvatarFallback>
<AvatarBadge className="bg-primary" />
</Avatar>
)
}
// Group
export function CaseTeam() {
return (
<AvatarGroup>
<Avatar>
<AvatarFallback>AL</AvatarFallback>
</Avatar>
<Avatar>
<AvatarFallback>OH</AvatarFallback>
</Avatar>
<AvatarGroupCount>+3</AvatarGroupCount>
</AvatarGroup>
)
}Examples
Default
The handling officer on a case, shown with initials next to their name and role.
With Fallback
When the image is missing or fails to load, AvatarFallback shows the person’s initials instead.
Sizes
Avatars come in sm, default and lg. For other sizes, override with a size-* class.
With Badge
AvatarBadge renders a status dot; colour it with a theme token via className. Icons inside the badge are shown on
default and lg avatars. Always pair the dot with a text status so the meaning doesn’t rely on colour alone.
Avatar Group
AvatarGroup overlaps avatars with a ring, and AvatarGroupCount shows the remaining count or an icon.
Custom Styles
A filled fallback, a ring marking the signed-in user, an icon for an organisation, and a custom size-14.
Subcomponents
Avatar- Root;size?: 'sm' | 'default' | 'lg'(default'default')AvatarImage- The image; hidden until it has loadedAvatarFallback- Shown while the image loads or when it fails;delay(ms) postpones it to avoid a flash on fast connectionsAvatarBadge- Status indicator placed insideAvatarAvatarGroup- Overlapping container for multiple avatarsAvatarGroupCount- Overflow indicator placed last inAvatarGroup
Accessibility
- Provide meaningful
alttext onAvatarImage - Status conveyed by
AvatarBadgecolor should also be available as text (e.g. a tooltip or visually hidden label) - Keep fallback text short and readable
Best Practices
-
Image Usage:
- Use appropriately sized images
- Provide meaningful alt text
- Handle image errors gracefully with a fallback
-
Fallback Content:
- Keep text short (1-2 characters)
- Ensure readable contrast
-
Sizing:
- Use the built-in sizes for consistency
- Keep a consistent size within a group
-
Grouping:
- Limit visible avatars and use
AvatarGroupCountfor the rest
- Limit visible avatars and use
Related Components
- Badge
- Tooltip