Avatar
An image element with a fallback for representing a user.
Playground
SJGBXL
SJGBXLInstallation
Install in any React + Tailwind project:
$npx shadcn@latest add https://twigs.globirdenergy.com.au/r/avatar.json
Source
The exact file the registry serves to consumers.
'use client';
import * as React from 'react';
import { cn } from '@/lib/utils';
type AvatarContextValue = { state: 'idle' | 'loaded' | 'error'; setState: (s: 'idle' | 'loaded' | 'error') => void };
const AvatarContext = React.createContext<AvatarContextValue | null>(null);
// Spec source: Figma "APP Re-design Phase 1" → page "Avatar ✅" (40x40, pill)
function Avatar({ className, ...props }: React.ComponentProps<'span'>) {
const [state, setState] = React.useState<'idle' | 'loaded' | 'error'>('idle');
return (
<AvatarContext.Provider value={{ state, setState }}>
<span
data-slot="avatar"
className={cn('relative flex size-10 shrink-0 overflow-hidden rounded-full', className)}
{...props}
/>
</AvatarContext.Provider>
);
}
function AvatarImage({ className, onLoad, onError, ...props }: React.ComponentProps<'img'>) {
const ctx = React.useContext(AvatarContext);
return ctx?.state === 'error' ? null : (
<img
data-slot="avatar-image"
className={cn('aspect-square size-full', className)}
onLoad={(e) => {
ctx?.setState('loaded');
onLoad?.(e);
}}
onError={(e) => {
ctx?.setState('error');
onError?.(e);
}}
{...props}
/>
);
}
function AvatarFallback({ className, ...props }: React.ComponentProps<'span'>) {
const ctx = React.useContext(AvatarContext);
if (ctx?.state === 'loaded') return null;
return (
<span
data-slot="avatar-fallback"
className={cn(
'bg-muted flex size-full items-center justify-center rounded-full text-sm font-medium',
className,
)}
{...props}
/>
);
}
export { Avatar, AvatarImage, AvatarFallback };