Skip to content

Avatar

A compact visual identity for a person or an organization, with an image, initials fallback and optional presence status.

SourceBase UIRegistry
MCJOLPRS
+8
import {
  Avatar,
  AvatarFallback,
  AvatarGroup,
  AvatarGroupCount,
  AvatarImage,
} from "@/components/ui/avatar"

import aurora from "./assets/aurora.svg"
import ember from "./assets/ember.svg"
import tide from "./assets/tide.svg"

export default function AvatarDemo() {
  return (
    <AvatarGroup>
      <Avatar size="lg">
        <AvatarImage src={aurora.src} alt="Maya Chen" />
        <AvatarFallback>MC</AvatarFallback>
      </Avatar>
      <Avatar size="lg">
        <AvatarImage src={ember.src} alt="Jonah Okafor" />
        <AvatarFallback>JO</AvatarFallback>
      </Avatar>
      <Avatar size="lg">
        <AvatarImage src={tide.src} alt="Lena Park" />
        <AvatarFallback>LP</AvatarFallback>
      </Avatar>
      <Avatar size="lg">
        <AvatarFallback>RS</AvatarFallback>
      </Avatar>
      <AvatarGroupCount>+8</AvatarGroupCount>
    </AvatarGroup>
  )
}

Anatomy

AL
GHKJMH
+4
  1. 1RootA circle for people, a rounded square for organizations. Five sizes, from 20 to 56px.
  2. 2BadgeAn optional status dot, ringed in the page color so it separates from the image.
  3. 3FallbackInitials on a neutral fill, shown while the image loads — or instead of it, when there is none.
  4. 4GroupOverlapping avatars, each ringed in the page color.
  5. 5CountHow many more people there are, sized to match the group.

Installation

$ pnpm dlx shadcn@latest add https://www.prfct.dev/r/avatar.json

Usage

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"
<Avatar>
  <AvatarImage src="/avatars/maya.png" alt="Maya Chen" />
  <AvatarFallback>MC</AvatarFallback>
</Avatar>

Always include an AvatarFallback. It renders while the image loads and permanently when the image is missing or fails, so a slow network or a deleted file never leaves a hole in the layout.

Examples

Sizes

Five sizes follow the control scale: xs 20, sm 24, default 32, lg 40 and xl 56 pixels. Initials scale with the avatar so they stay optically centered.

MCMCMCMCMC
MCMCMCMCMC
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"

import aurora from "./assets/aurora.svg"

const sizes = ["xs", "sm", "default", "lg", "xl"] as const

export default function AvatarSizes() {
  return (
    <div className="flex flex-col items-center gap-6">
      <div className="flex items-end gap-4">
        {sizes.map((size) => (
          <Avatar key={size} size={size}>
            <AvatarImage src={aurora.src} alt="Maya Chen" />
            <AvatarFallback>MC</AvatarFallback>
          </Avatar>
        ))}
      </div>
      <div className="flex items-end gap-4">
        {sizes.map((size) => (
          <Avatar key={size} size={size}>
            <AvatarFallback>MC</AvatarFallback>
          </Avatar>
        ))}
      </div>
    </div>
  )
}
SizeUse it for
xs, smInline mentions, dense tables, stacked assignees.
defaultLists, comments, menus, table rows.
lgCards, account switchers, member lists with two lines of text.
xlProfile headers and settings pages — one per view.

Fallback

When there is no image, show two uppercase initials — given name and family name. For people you know nothing about, use a neutral icon and give it an accessible name.

PRImage
PRInitials
Unknown userAnonymous
import { UserIcon } from "lucide-react"

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"

import dusk from "./assets/dusk.svg"

export default function AvatarFallbackExample() {
  return (
    <div className="flex items-center gap-6">
      <div className="flex flex-col items-center gap-2">
        <Avatar size="lg">
          <AvatarImage src={dusk.src} alt="Priya Raman" />
          <AvatarFallback>PR</AvatarFallback>
        </Avatar>
        <span className="text-xs text-muted-foreground">Image</span>
      </div>
      <div className="flex flex-col items-center gap-2">
        <Avatar size="lg">
          <AvatarFallback>PR</AvatarFallback>
        </Avatar>
        <span className="text-xs text-muted-foreground">Initials</span>
      </div>
      <div className="flex flex-col items-center gap-2">
        <Avatar size="lg">
          <AvatarFallback>
            <UserIcon className="size-4" aria-hidden="true" />
            <span className="sr-only">Unknown user</span>
          </AvatarFallback>
        </Avatar>
        <span className="text-xs text-muted-foreground">Anonymous</span>
      </div>
    </div>
  )
}
Delaying the fallback
On fast connections the initials can flash for a frame before the image appears. Pass delay={600} to AvatarFallback to show it only when loading is genuinely slow.

Shape

Circles are for people; rounded squares are for things people belong to — workspaces, organizations, apps. The shape carries meaning, so keep it consistent across the product.

MC
Maya ChenPerson · circle
NW
NorthwindWorkspace · square
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar"

import aurora from "./assets/aurora.svg"
import moss from "./assets/moss.svg"

export default function AvatarShape() {
  return (
    <div className="grid justify-items-start gap-6 sm:grid-flow-col sm:gap-10">
      <div className="flex items-center gap-3">
        <Avatar size="lg">
          <AvatarImage src={aurora.src} alt="Maya Chen" />
          <AvatarFallback>MC</AvatarFallback>
        </Avatar>
        <div className="flex flex-col whitespace-nowrap">
          <span className="text-sm font-medium">Maya Chen</span>
          <span className="text-xs text-muted-foreground">Person · circle</span>
        </div>
      </div>
      <div className="flex items-center gap-3">
        <Avatar size="lg" shape="square">
          <AvatarImage src={moss.src} alt="Northwind" />
          <AvatarFallback>NW</AvatarFallback>
        </Avatar>
        <div className="flex flex-col whitespace-nowrap">
          <span className="text-sm font-medium">Northwind</span>
          <span className="text-xs text-muted-foreground">
            Workspace · square
          </span>
        </div>
      </div>
    </div>
  )
}

Status

AvatarBadge sits on the bottom-right edge with a ring in the page color, so it reads on any background. It defaults to the success color for online; recolor it with a semantic token for other states, and always include a text label for screen readers.

MCOnlineJOAwayLPOfflineRSVerified
import { CheckIcon } from "lucide-react"

import {
  Avatar,
  AvatarBadge,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/avatar"

import aurora from "./assets/aurora.svg"
import ember from "./assets/ember.svg"
import tide from "./assets/tide.svg"

export default function AvatarWithBadge() {
  return (
    <div className="flex items-center gap-8">
      <Avatar size="lg">
        <AvatarImage src={aurora.src} alt="Maya Chen" />
        <AvatarFallback>MC</AvatarFallback>
        <AvatarBadge>
          <span className="sr-only">Online</span>
        </AvatarBadge>
      </Avatar>
      <Avatar size="lg">
        <AvatarImage src={ember.src} alt="Jonah Okafor" />
        <AvatarFallback>JO</AvatarFallback>
        <AvatarBadge className="bg-warning-9">
          <span className="sr-only">Away</span>
        </AvatarBadge>
      </Avatar>
      <Avatar size="lg">
        <AvatarImage src={tide.src} alt="Lena Park" />
        <AvatarFallback>LP</AvatarFallback>
        <AvatarBadge className="bg-gray-9">
          <span className="sr-only">Offline</span>
        </AvatarBadge>
      </Avatar>
      <Avatar size="xl">
        <AvatarFallback>RS</AvatarFallback>
        <AvatarBadge className="bg-brand text-brand-foreground">
          <CheckIcon aria-hidden="true" />
          <span className="sr-only">Verified</span>
        </AvatarBadge>
      </Avatar>
    </div>
  )
}

Group

Stack avatars to show who is involved without listing everyone. Show three to five, then summarize the rest with AvatarGroupCount. Each avatar gets a ring in the page color so overlaps stay crisp.

MCJOLPPR
+12
MCJOLPPR
+12
MCJOLPPR
+12
import {
  Avatar,
  AvatarFallback,
  AvatarGroup,
  AvatarGroupCount,
  AvatarImage,
} from "@/components/ui/avatar"

import aurora from "./assets/aurora.svg"
import dusk from "./assets/dusk.svg"
import ember from "./assets/ember.svg"
import tide from "./assets/tide.svg"

const people = [
  { name: "Maya Chen", initials: "MC", image: aurora.src },
  { name: "Jonah Okafor", initials: "JO", image: ember.src },
  { name: "Lena Park", initials: "LP", image: tide.src },
  { name: "Priya Raman", initials: "PR", image: dusk.src },
]

export default function AvatarGroupExample() {
  return (
    <div className="flex flex-col items-center gap-6">
      {(["sm", "default", "lg"] as const).map((size) => (
        <AvatarGroup
          key={size}
          aria-label="Project members: Maya Chen, Jonah Okafor, Lena Park, Priya Raman and 12 others"
        >
          {people.map((person) => (
            <Avatar key={person.name} size={size}>
              <AvatarImage src={person.image} alt={person.name} />
              <AvatarFallback>{person.initials}</AvatarFallback>
            </Avatar>
          ))}
          <AvatarGroupCount>+12</AvatarGroupCount>
        </AvatarGroup>
      ))}
    </div>
  )
}

With text

Most of the time an avatar sits next to a name. Because the name is already visible, the avatar is decorative: give the image an empty alt so assistive technology doesn't announce the name twice.

MCOnline
Maya Chenmaya@northwind.dev
Owner
import {
  Avatar,
  AvatarBadge,
  AvatarFallback,
  AvatarImage,
} from "@/components/ui/avatar"
import { Badge } from "@/components/ui/badge"

import aurora from "./assets/aurora.svg"

export default function AvatarWithText() {
  return (
    <div className="flex w-full max-w-sm items-center gap-3 rounded-xl border bg-card p-4 shadow-raised">
      <Avatar size="lg">
        <AvatarImage src={aurora.src} alt="" />
        <AvatarFallback>MC</AvatarFallback>
        <AvatarBadge>
          <span className="sr-only">Online</span>
        </AvatarBadge>
      </Avatar>
      <div className="flex min-w-0 flex-1 flex-col">
        <span className="truncate text-sm font-medium">Maya Chen</span>
        <span className="truncate text-body-xs text-muted-foreground">
          maya@northwind.dev
        </span>
      </div>
      <Badge variant="secondary">Owner</Badge>
    </div>
  )
}

Guidelines

When to use

  • To help people recognize who did something: comment authors, assignees, collaborators, account owners.
  • To identify a workspace, organization or connected app in switchers and lists.

When not to use

  • For decorative or editorial imagery — use a regular image.
  • For objects that aren't people or organizations, such as files or integrations in a list — use Item with an icon.
  • As the only way to identify someone in a dense table. Pair it with a name, or make the name available in a tooltip.

Consistent identity

MCJONW
DoCircles for people, squares for workspaces — the shape tells you what you're looking at.
MCJOLP
Don’tMixing shapes for the same kind of entity makes people look like organizations.

Initials

AK
DoTwo uppercase letters: first letter of the given and the family name.
aik
Don’tThree or more characters, lowercase, or emoji — they overflow at small sizes and read as noise.

Images

  • Use a square source at least twice the rendered size (112px for xl) so avatars stay sharp on high-density screens.
  • Crop around the face or logo; the avatar applies object-cover and clips to its shape.
  • Don't add borders — every avatar already has a hairline inner ring that keeps light images from dissolving into light surfaces.

Accessibility

  • Name the image. AvatarImage renders an <img>: set alt to the person's or organization's name. When the name is visible next to the avatar, set alt="" and add aria-hidden to the Avatar so the initials aren't announced as letters.
  • Status needs words. The badge is color and position only. Put a visually hidden label inside it (<span className="sr-only">Online</span>), and show presence as text wherever it matters.
  • Groups need a summary. AvatarGroup is a group, so a label on it is announced. Give it an aria-label that lists the people or states how many there are — "Project members: Maya Chen, Jonah Okafor and 12 others".
  • Contrast. Fallback initials (gray-11 on gray-3) meet 4.5:1 in both modes.

API reference

Avatar

The root. Accepts every prop of the Base UI Avatar Root.

PropTypeDefault
size

20, 24, 32, 40 or 56 pixels. Fallback text and badge scale with it.

"xs" | "sm" | "default" | "lg" | "xl""default"
shape

Circle for people, rounded square for organizations and apps.

"circle" | "square""circle"
render

Replaces the rendered span element.

ReactElement | (props, state) => ReactElementNo default

AvatarImage

PropTypeDefault
src

Image URL. Loaded before it is shown; the fallback renders until then.

stringNo default
alt

Always set it: the person's or organization's name, or an empty string when the name is visible nearby.

stringNo default
onLoadingStatusChange

Called whenever the image's loading status changes.

(status: "idle" | "loading" | "loaded" | "error") => voidNo default
keepMounted

Keeps the img element mounted and loads it in place — needed for lazy loading and optimized image components.

booleanfalse

AvatarFallback

PropTypeDefault
delay

Milliseconds to wait before showing the fallback, to avoid a flash on fast connections.

number0
children

Two-letter initials or an icon.

ReactNodeNo default

AvatarBadge

A <span> positioned on the avatar's edge. Defaults to the success color; override with a background token such as bg-warning-9. Icons inside are sized for you from default up and hidden at xs and sm, where the dot is too small to hold one.

AvatarGroup

A <div> with role="group" that overlaps its avatars and rings them in the page color. Accepts all div props, including aria-label.

AvatarGroupCount

A <div> for the "+N" summary at the end of a group. It follows the size of the avatars in the group automatically.