Avatar

A person's image with initials as the fallback.

Example

ALGH

Installation

pnpm dlx shadcn@latest add superzero11/uiset/avatar

It lands in components/ui/avatar.tsx, with UISet’s root (uiset). Wrap your app in <UISet> once, and every component takes its skin.

What UISet adds

The component is UI SDK’s own, with its parts, props and names, so everything below works as UI SDK documents it. UISet adds the look and checks it:

  • Styles in five skins, each in light and dark, from CSS variables you can override.
  • Text at 4.5:1 contrast and control edges and focus rings at 3:1, in every skin and theme, checked with axe in tests.
  • Haptic cues on phones, and sound cues when you set sound on <UISet>.
  • Examples that name every control, so agents can find it (see Agents below).

The API reference and accessibility notes below are UI SDK’s, from its docs for @ui-sdk/react (Apache-2.0). Avatar on ui-sdk.dev has more usage examples. Your coding agent can read the same page at node_modules/@ui-sdk/react/docs/components/avatar.md.

API Reference

Props and callbacks are the same as below, as React props.

Avatar

  • onLoadingStatusChange? (((status: AvatarLoadingStatus, details: ChangeDetails<AvatarLoadingStatusChangeReason>) => void)): Runs when the image's loading status changes: "loading" when it gets a source, "loaded", "error", and "idle" when it leaves the page. details.reason says why, and details.event is the image's load or error event, or undefined when the status was known at once, like for a cached image.
  • debug? (boolean): Logs every state change with its reason to the console, in development builds.

AvatarImage

The image. Renders a real img, so the browser can fetch it before hydration, and accepts its attributes, such as srcSet, crossOrigin and referrerPolicy.

AvatarFallback

  • delay? (number): How long to wait, in milliseconds, before showing the fallback, so a fast image appears without the fallback flashing first. Without one, the fallback shows at once, server markup included.

State

What render functions, state-based class names and slots receive. Every part gets the same state.

  • loadingStatus (AvatarLoadingStatus): Where the image is: "idle", "loading", "loaded" or "error".

Reasons

details.reason says why the status changed.

  • image-load (Event | undefined): An image loaded. No event when it was already in the browser's cache.
  • image-error (Event | undefined): An image failed to load, or has no source. No event when that was known at once.
  • src-change (none): An image got a new source, or left the page, so its status starts over.

Events

Callbacks get the new status first, then the details.

PropOnReceives
onLoadingStatusChangeAvatar(status, details): the image's status, the reason and details.event

Accessibility

Give the image meaningful alt text. Include the person's name in the fallback too, using visually hidden text if you only want to show initials.

When a visible name beside the avatar already identifies the person, use an empty alt and hide decorative fallback content from screen readers.

  • An image that loads replaces the fallback.
  • An image that fails keeps the fallback.
  • An avatar without an image shows its fallback.
  • A fallback with a delay waits before showing.
  • The image's loading status is reported.
  • Removing the image brings the fallback back.
  • A new source shows its image once it loads.
  • The image is fetched with its own referrer policy.
  • A cached image doesn't flash the fallback when the avatar mounts again.
  • A new source keeps showing the previous image until it loads.
  • The image is fetched once, even when it can't be cached.

Runs in React, Vue and web components.

Agents

UI SDK’s agent bridge finds this component by its accessible name: give it a visible label or an aria-label, and an agent can read its state and call its actions without screenshots. Building with AI agents shows how to connect one.

This is agent.snapshot() for the example above, read live from this page. Change something in the example, then read it again: the values follow.

agent.snapshot()
[
  {
    "ref": "c8",
    "component": "avatar",
    "actions": [],
    "label": "Ada Lovelace"
  },
  {
    "ref": "c9",
    "component": "avatar",
    "actions": [],
    "label": "Grace Hopper"
  }
]