Skip to content

Avatar component

Avatar renders a user's profile image with a graceful fallback to initials (derived from name) or an icon when there's no src, or while the image is still loading. A companion AvatarGroup component stacks several Avatars with automatic overflow counting, for things like a list of collaborators on a document.

Data Display19 propsAccessibility notes

Playground

Change a prop on the right and the preview updates. Switch to Code to copy what you see.

Recipes

Copy-paste patterns for the ways Avatar actually gets used — each one a real, self-contained example.

Image avatar

Pass src for a photo; name gives it an accessible label and an initials fallback if the image fails to load.

<Avatar src="/hero/person-woman.jpg" name="Sara Whitfield" />
<Avatar src="/hero/person-man.jpg" name="Marcus Lee" />

Initials fallback

With no src, Avatar renders the name's initials — colour it with the color prop for variety across a list.

<Avatar name="Sara Whitfield" color="primary" />
<Avatar name="Marcus Lee" color="success" />
<Avatar name="Ada Lovelace" color="warning" />

Sizes

Six size tokens (or a raw pixel number) — small for dense lists, large for profile headers.

<Avatar name="AB" size="xs" />
<Avatar name="AB" size="sm" />
<Avatar name="AB" size="md" />
<Avatar name="AB" size="lg" />
<Avatar name="AB" size="xl" />

Presence status

status adds an online/offline/dnd indicator dot — presence without a separate element.

<Avatar src="/hero/person-woman.jpg" name="Sara" status="online" />
<Avatar name="Marcus Lee" status="dnd" />
<Avatar name="Ada Lovelace" status="offline" />

Avatar group

AvatarGroup overlaps avatars and collapses the overflow into a “+N” once past max — the standard “who's on this” cluster.

<AvatarGroup max={3}>
  <Avatar src="/hero/person-woman.jpg" name="Sara" />
  <Avatar src="/hero/person-man.jpg" name="Marcus" />
  <Avatar name="Ada Lovelace" />
  <Avatar name="Grace Hopper" />
  <Avatar name="Alan Turing" />
</AvatarGroup>

Group with name tooltips

Avatar's own tooltip prop wraps it in a Tooltip -- pass true (or just the prop with no value) to derive the label from name, or a string/object for full control. AvatarGroup clones its size/color/radius/... group defaults onto each Avatar before that wrapping happens, so tooltip composes with it with no extra wiring.

<AvatarGroup max={4}>
  <Avatar tooltip src="/hero/person-woman.jpg" name="Sara Whitfield" />
  <Avatar tooltip src="/hero/person-man.jpg" name="Marcus Lee" />
  <Avatar tooltip name="Ada Lovelace" />
  <Avatar tooltip name="Grace Hopper" />
  <Avatar tooltip name="Alan Turing" />
</AvatarGroup>

Image with automatic fallback

Pass src for the image and name so there's something sensible to fall back to — Avatar derives initials from name automatically if the image fails to load or hasn't loaded yet. If you don't have a name either, pass icon for a generic fallback instead of leaving it blank.

<Avatar src={user.avatarUrl} name={user.fullName} />
<Avatar name="Ada Lovelace" /> {/* no src -- renders "AL" */}
<Avatar icon={<UserIcon />} /> {/* no src, no name -- renders the icon */}

Status indicator

status renders a small colored dot in the corner for a presence indicator (online, offline, dnd) — built on the same positioning machinery Badge uses for its own dot mode, so it stays correctly anchored across every avatar size.

<Avatar src={user.avatarUrl} name={user.fullName} status="online" />

Fully custom fallback

For anything beyond initials or a single icon — a placeholder illustration, a different fallback per user type — pass fallback with any ReactNode; it takes priority over both icon and the derived initials. showFallback forces the fallback to render even while a valid image is still loading, useful if you'd rather show a stable placeholder than a flash of empty space.

Rendering through a different image component

By default the image renders as a plain <img>. Pass ImgComponent to render it through something else instead — next/image, for instance — with imgProps carrying whatever extra props that component needs (width/height, loader, etc.).

<Avatar
  src={user.avatarUrl}
  name={user.fullName}
  ImgComponent={Image}
  imgProps={{ width: 40, height: 40 }}
/>

Grouping avatars with AvatarGroup

AvatarGroup stacks its children with a configurable overlap and shows a "+N" overflow count once there are more avatars than max — pass total instead of relying on children.length if the real total (e.g. from a paginated API) is larger than what's actually rendered.

import { Avatar, AvatarGroup } from "oks-ui";

<AvatarGroup max={4} total={12}>
  {collaborators.map((c) => (
    <Avatar key={c.id} src={c.avatarUrl} name={c.name} />
  ))}
</AvatarGroup>

Props

The full public API for Avatar. Every prop is stable and intentionally typed.

Content

name

string

default: —

Used to derive fallback initials.

src

string

default: —

Image source URL.

icon

ReactNode

default: —

Custom fallback icon, used when name/initials aren't available.

fallback

ReactNode

default: —

Fully custom fallback content, overrides icon/initials.

showFallback

boolean

default: false

Forces the fallback (initials/icon) even while an image is loading.

AvatarGroup

{ max?, total?, size?, color?, radius?, isGrid?, isDisabled?, isBordered?, overlap?, renderCount?, classNames? }

default: —

Stacks Avatar children and collapses whatever's past max (default 5) into a "+N" -- pass total instead of relying on children.length if you're not rendering every avatar. size/color/radius/isBordered/isDisabled cascade onto every child that doesn't set its own (a child's tooltip prop is untouched, so per-avatar tooltips still work). overlap is the px overlap between avatars (default 14); isGrid wraps instead of stacking; renderCount(count) customizes the overflow label.

Appearance

size

"xs" | "xs-sm" | "sm" | "md" | "lg" | "xl" | number

default: md

Token size, or a number for an exact px size (font-size scales ~0.4x; the status Badge maps to the nearest token bucket). AvatarGroup forces its size onto children.

color

"default" | "primary" | "secondary" | "info" | "success" | "warning" | "danger"

default: default

Semantic color for the fallback background.

radius

"none" | "xs" | "xs-sm" | "sm" | "md" | "lg" | "full"

default: full

Border radius.

colorDepth

50 | 100 | ... | 950

default: 500

Shade depth for the selected color.

isBordered

boolean

default: false

Adds a ring border around the avatar.

Behavior & states

isDisabled

boolean

default: false

Dims the avatar and disables interaction.

isFocusable

boolean

default: false

Makes the avatar keyboard-focusable.

tooltip

ReactNode | boolean | (Omit<TooltipProps, "children"|"content"> & { content?: ReactNode })

default: false

Wraps the avatar in a Tooltip. A plain value is shorthand content; bare true derives content from name; an object accepts any Tooltip prop plus an optional content override.

status

"online" | "offline" | "dnd"

default: —

Status dot indicator.

ImgComponent

ElementType

default: "img"

Renders the image through a different component (e.g. next/image).

imgProps

ComponentPropsWithoutRef<ImgComponent>

default: —

Extra props forwarded to the image element/component.

Styling

classNames

Partial<Record<AvatarSlot, string>>

default: —

Per-slot class overrides (base/img/fallback/name/icon/status).

className

string

default: —

Class applied to the root element.

Accessibility

  • name doubles as the source for derived initials and should be the person's real display name — it's what a screen reader falls back to describing when there's no explicit label.
  • isFocusable makes an otherwise purely decorative avatar keyboard-reachable, for cases where the avatar itself is interactive (opens a profile card, a menu) rather than just illustrative.
  • status is a purely visual dot by default — if the presence state needs to be announced, pair it with your own aria-label or adjacent text, since color alone doesn't reach assistive technology.

Best practices

  • Always pass name even when src is set — it's the fallback the user actually sees if the image 404s, and costs nothing when the image loads fine.
  • Use showFallback when a slow image load would otherwise cause a visible flash from blank to loaded; it keeps the layout looking intentional the whole time.
  • In AvatarGroup, set max low enough that the stack stays readable — beyond 4-5 overlapping avatars the individual faces become hard to distinguish anyway, and the overflow count communicates the rest just fine.
  • Reach for ImgComponent + imgProps when your app already has an optimized image component (next/image, a CDN-aware wrapper) — piping avatars through it keeps their loading behavior consistent with the rest of the app's images.