"use client"; import { useState } from "react"; import { cn } from "@/lib/utils"; export interface AvatarProps { src?: string; alt?: string; /** Shown while there is no image, or once it fails to load. */ fallback: string; className?: string; } /** A round avatar. The image fades in on load and crossfades to the fallback if it fails. */ export function Avatar({ src, alt = "", fallback, className }: AvatarProps) { const [loaded, setLoaded] = useState(false); const [errored, setErrored] = useState(false); const showImage = Boolean(src) && !errored; return ( {fallback} {showImage ? ( // A plain img, not next/image: this ships as source a consumer copies // into their own app, which may not have next/image configured. // biome-ignore lint/performance/noImgElement: portable across non-Next apps. {alt} setLoaded(true)} onError={() => setErrored(true)} className={cn( "absolute inset-0 h-full w-full object-cover opacity-0 transition-opacity duration-150 ease-out", loaded && "opacity-100", )} /> ) : null} ); }