Skip to main content
Gremorie

Avatar

User or entity portrait with graceful image fallback. Sizes, presence badges, and overlapping groups.

Overview

Avatar is the user or entity portrait primitive. It wraps Radix Avatar, which handles the image-load lifecycle for you: while the source loads, AvatarFallback renders; if the load errors or src is missing, the fallback stays. The Avatar never renders as a broken image or empty circle.

Three sizes ship on the root (sm, default, lg) and propagate to subcomponents via data-size. For richer compositions, drop in AvatarBadge for a presence dot or status indicator, and use AvatarGroup plus AvatarGroupCount for overlapping stacks.

Preview

KAKANG
'use client';import { Avatar, AvatarFallback, AvatarImage } from '@gremorie/rx-display';export function AvatarPreview() {  return (    <div className="flex items-center gap-3">      <Avatar>        <AvatarImage          src="https://avatars.githubusercontent.com/u/41934312?v=4"          alt="@kalvner"        />        <AvatarFallback>KA</AvatarFallback>      </Avatar>      <Avatar>        <AvatarFallback>KA</AvatarFallback>      </Avatar>      <Avatar>        <AvatarFallback>NG</AvatarFallback>      </Avatar>    </div>  );}

Anatomy

Avatar                  root circle; sets size via data-size
├─ AvatarImage          the portrait; hidden until loaded
├─ AvatarFallback       initials/icon shown while loading or on error
└─ AvatarBadge          small status dot anchored bottom-right
AvatarGroup             overlapping -space-x-2 stack of Avatars
├─ Avatar               each stacked portrait (see above)
└─ AvatarGroupCount     "+N" overflow chip

Installation

bash npx gremorie@latest add rx-avatar
bash pnpm dlx gremorie@latest add rx-avatar
bash yarn dlx gremorie@latest add rx-avatar
bash bunx --bun gremorie@latest add rx-avatar

Usage

import {
  Avatar,
  AvatarImage,
  AvatarFallback,
} from "@gremorie/rx-display";

export function Example() {
  return (
    <Avatar>
      <AvatarImage src="https://github.com/kalvner.png" alt="@kalvner" />
      <AvatarFallback>KA</AvatarFallback>
    </Avatar>
  );
}

The Angular edition of this component ships from source today (see the workbench for the side-by-side); its registry entry is coming next.

API

<Avatar>

Root container. Wraps Radix Avatar.Root.

PropTypeDefaultDescription
size"sm" | "default" | "lg""default"Sets the root dimensions (size-6, size-8, size-10) and propagates to subcomponents via data-size.
classNamestring-Extra classes. Use to override the default size-* when you need an in-between or larger size.

For sizes beyond lg, pass an explicit size-12 or larger via className.

<AvatarImage>

The image. Wraps Radix Avatar.Image. Forwards all img props including src, alt, onLoadingStatusChange.

PropTypeDefaultDescription
srcstring-Image URL. Required for the image to render.
altstring-Alt text. Required for accessibility.

<AvatarFallback>

The fallback content (typically initials). Wraps Radix Avatar.Fallback.

PropTypeDefaultDescription
delayMsnumber-Delays the fallback render to avoid a flash while the image loads.

<AvatarBadge>

Presence dot or status indicator. Positions absolutely in the bottom-right corner of the Avatar with a ring matching the page background.

Sizes adapt automatically to the parent Avatar's data-size - the badge stays proportional.

<AvatarGroup>

Container for overlapping Avatars. Applies -space-x-2 and a 2px ring on each child Avatar so they read as a stack.

<AvatarGroupCount>

Trailing "+N" indicator for hidden Avatars in a group. Inherits the group's size from group-has-data-[size=*]/avatar-group selectors.

Composition

  1. <Avatar> is the container - always renders a circle.
  2. Inside Avatar: place <AvatarImage> first, then <AvatarFallback> as a safety net. Both must be siblings; Radix coordinates the lifecycle.
  3. <AvatarBadge> is optional and absolute-positioned - drop it as the last child of Avatar.
  4. Groups: wrap multiple Avatars in <AvatarGroup> and end with <AvatarGroupCount> if you have overflow.

Variations

Sizes

SMMDLG
'use client';import { Avatar, AvatarFallback, AvatarImage } from '@gremorie/rx-display';export function AvatarSizesPreview() {  return (    <div className="flex items-center gap-3">      <Avatar size="sm">        <AvatarImage          src="https://avatars.githubusercontent.com/u/41934312?v=4"          alt="@kalvner"        />        <AvatarFallback>SM</AvatarFallback>      </Avatar>      <Avatar>        <AvatarImage          src="https://avatars.githubusercontent.com/u/41934312?v=4"          alt="@kalvner"        />        <AvatarFallback>MD</AvatarFallback>      </Avatar>      <Avatar size="lg">        <AvatarImage          src="https://avatars.githubusercontent.com/u/41934312?v=4"          alt="@kalvner"        />        <AvatarFallback>LG</AvatarFallback>      </Avatar>    </div>  );}

Three sizes ship on the root: sm (size-6), default (size-8), and lg (size-10). The size propagates to subcomponents via data-size.

Fallback only

KANGSP
'use client';import { Avatar, AvatarFallback, AvatarImage } from '@gremorie/rx-display';export function AvatarFallbackPreview() {  return (    <div className="flex items-center gap-3">      <Avatar>        <AvatarImage src="/broken-image.png" alt="@kalvner" />        <AvatarFallback>KA</AvatarFallback>      </Avatar>      <Avatar>        <AvatarFallback>NG</AvatarFallback>      </Avatar>      <Avatar>        <AvatarFallback>SP</AvatarFallback>      </Avatar>    </div>  );}

When the image source is missing or fails to load, AvatarFallback renders automatically - typically initials. The Avatar never shows a broken image.

Overlapping group

KANGSP
+3
'use client';import {  Avatar,  AvatarFallback,  AvatarGroup,  AvatarGroupCount,} from '@gremorie/rx-display';export function AvatarGroupPreview() {  return (    <AvatarGroup>      <Avatar>        <AvatarFallback>KA</AvatarFallback>      </Avatar>      <Avatar>        <AvatarFallback>NG</AvatarFallback>      </Avatar>      <Avatar>        <AvatarFallback>SP</AvatarFallback>      </Avatar>      <AvatarGroupCount>+3</AvatarGroupCount>    </AvatarGroup>  );}

AvatarGroup applies -space-x-2 and a background ring so the avatars read as a stack. End with AvatarGroupCount to show overflow.

With presence badge

KA
'use client';import {  Avatar,  AvatarBadge,  AvatarFallback,  AvatarImage,} from '@gremorie/rx-display';export function AvatarBadgePreview() {  return (    <Avatar size="lg">      <AvatarImage        src="https://avatars.githubusercontent.com/u/41934312?v=4"        alt="@kalvner"      />      <AvatarFallback>KA</AvatarFallback>      <AvatarBadge className="bg-emerald-500" />    </Avatar>  );}

AvatarBadge positions absolutely in the bottom-right corner and adapts its size to the parent Avatar's data-size.

Accessibility

  • AvatarImage requires alt: pass the user or entity name. Screen readers announce the alt; the fallback is hidden from AT while the image is present.
  • Fallback content: when the image fails, the fallback text (typically initials) becomes the announced label. Keep it meaningful - "KA" announces as "K A"; use the full name in aria-label on the Avatar root if initials are too cryptic.
  • Decorative use: when an Avatar is purely decorative (e.g. in a marketing illustration), pass alt="" on AvatarImage and add aria-hidden="true" on the Avatar root so AT skips it.
  • Presence badges: AvatarBadge is a <span> with no semantics. Convey presence through context (e.g. a sibling tooltip or text), not color alone.
  • Group counts: AvatarGroupCount announces as "+3" or whatever you put inside - wrap with text like "and 3 more" for clearer screen-reader output.
  • Badge - inline status pill (separate primitive from AvatarBadge).
  • Tooltip - pairs with Avatar to reveal the full name on hover.
  • Card - typical host for avatar plus name in profile cards.

On this page