Avatar

An image element with a fallback for representing the user.

The Avatar component displays a user's profile image with an automatic fallback when the image fails to load or is not provided. It supports multiple sizes and can display custom fallback content. Built from scratch using React and native HTML img elements. No UI library dependencies.

Code

TypeScript: Copy this code into components/ui/avatar.tsx:

tsx
import * as React from "react"
import { cva, type VariantProps } from "class-variance-authority"

import { cn } from "@/lib/utils"

const avatarVariants = cva(
  "relative flex shrink-0 overflow-hidden rounded-full border-2 border-foreground neobrutalism-shadow",
  {
    variants: {
      size: {
        default: "h-10 w-10",
        sm: "h-8 w-8",
        lg: "h-16 w-16",
        xl: "h-24 w-24",
      },
    },
    defaultVariants: {
      size: "default",
    },
  }
)

export interface AvatarProps
  extends React.ImgHTMLAttributes<HTMLImageElement>,
    VariantProps<typeof avatarVariants> {
  fallback?: React.ReactNode
}

const Avatar = React.forwardRef<HTMLImageElement, AvatarProps>(
  ({ className, size, src, alt, fallback, ...props }, ref) => {
    const [error, setError] = React.useState(false)

    if (error || !src) {
      return (
        <div
          className={cn(avatarVariants({ size, className }))}
          ref={ref as React.Ref<HTMLDivElement>}
        >
          <div className="flex h-full w-full items-center justify-center bg-muted text-muted-foreground text-sm font-bold">
            {fallback || (alt ? alt.charAt(0).toUpperCase() : "?")}
          </div>
        </div>
      )
    }

    return (
      <img
        ref={ref}
        src={src}
        alt={alt}
        className={cn(avatarVariants({ size, className }))}
        onError={() => setError(true)}
        {...props}
      />
    )
  }
)
Avatar.displayName = "Avatar"

export { Avatar, avatarVariants }

JavaScript: Copy this code into components/ui/avatar.jsx:

jsx
import * as React from "react"
import { cva } from "class-variance-authority"

import { cn } from "@/lib/utils"

const avatarVariants = cva(
  "relative flex shrink-0 overflow-hidden rounded-full border-2 border-foreground neobrutalism-shadow",
  {
    variants: {
      size: {
        default: "h-10 w-10",
        sm: "h-8 w-8",
        lg: "h-16 w-16",
        xl: "h-24 w-24",
      },
    },
    defaultVariants: {
      size: "default",
    },
  }
)

const Avatar = React.forwardRef(
  ({ className, size, src, alt, fallback, ...props }, ref) => {
    const [error, setError] = React.useState(false)

    if (error || !src) {
      return (
        <div
          className={cn(avatarVariants({ size, className }))}
          ref={ref}
        >
          <div className="flex h-full w-full items-center justify-center bg-muted text-muted-foreground text-sm font-bold">
            {fallback || (alt ? alt.charAt(0).toUpperCase() : "?")}
          </div>
        </div>
      )
    }

    return (
      <img
        ref={ref}
        src={src}
        alt={alt}
        className={cn(avatarVariants({ size, className }))}
        onError={() => setError(true)}
        {...props}
      />
    )
  }
)
Avatar.displayName = "Avatar"

export { Avatar, avatarVariants }

Usage

TypeScript:

tsx
import { Avatar } from "@/components/ui/avatar"

<Avatar src="/user.jpg" alt="User" />
<Avatar src="/user.jpg" alt="User" size="lg" />
<Avatar fallback="JD" size="sm" />

JavaScript:

jsx
import { Avatar } from "@/components/ui/avatar"

<Avatar src="/user.jpg" alt="User" />
<Avatar src="/user.jpg" alt="User" size="lg" />
<Avatar fallback="JD" size="sm" />

Make sure you also have the lib/utils.ts file with the cn helper function.

Examples

UserUserUserUser
JD
AB
CD
EF