@xsolla/xui-avatar
v0.192.0
Published
A cross-platform React avatar component that displays a user image, icon, or text initials. Supports notification badges and grouping via `AvatarGroup`. <!-- BEGIN:xui-mcp-instructions:avatar --> A visual representation of a user or entity. Supports three
Readme
Avatar
A cross-platform React avatar component that displays a user image, icon, or text initials. Supports notification badges and grouping via AvatarGroup.
A visual representation of a user or entity. Supports three content types — initials, icon, and image — across six sizes and two tonal variants. Can be circular or square, and optionally interactive with a hover state.
When to use
- To identify a user in a profile header, comment thread, or account menu
- In lists, tables, or cards where a visual identity marker helps scan rows quickly
- In AvatarGroup to represent multiple users in a compact space
- As a touch target for opening a user profile or account dropdown
When not to use
- As a generic decorative image — use an IconWrapper component instead
- When the entity being represented is not a person or named account (e.g. a product, category, or abstract concept)
- When you need a logo — use a BrandLogo or Icon Wrapper with a brand asset instead
Content guidelines
Text type: use the first letter of the first name and the first letter of the last name (e.g. "J" or "JD"). Never use more than two characters.
Image type: always use a square source image; the component clips it to fit. Avoid images with important content near the edges.
Alt text: always provide a meaningful aria-label or alt string for the Avatar — e.g. the user's full name — regardless of content type.
Behaviour guidelines
Fallback chain — always define a fallback order for content type: attempt to load Image first; if the image fails to load or is unavailable, fall back to Text (initials); if the name is also unknown, fall back to Icon. Never leave the container empty.
Image loading — while the photo is loading, render the Icon variant as a placeholder. Avoid layout shift by reserving the Avatar's dimensions before the image resolves.
Interactive vs decorative — an Avatar that opens a profile, menu, or action sheet must have Hover=True and receive keyboard focus. An Avatar used purely for identification (e.g. in a comment or log entry) must not be focusable and must not have hover styling.
Click / tap target — when interactive, the entire Avatar container is the tap target. Do not wrap only the image or icon in a link while leaving the container unresponsive.
Tooltip on hover — where the initials or icon may be hard to identify, show a tooltip with the user's full name on hover and on focus.
Badge visibility — the Badge reflects live status data (e.g. online / offline). It should update in real time without a full component remount. If status data is unavailable, hide the Badge (Badge=False) rather than showing a stale or unknown state.
Square vs circle — do not switch shape dynamically for the same entity. The shape is a semantic signal (circle = person, square = bot/service) and must remain stable across contexts where the same Avatar appears.
Accessibility
Provide aria-label with the user's name on every Avatar instance.
If the Avatar is interactive, it must receive keyboard focus and respond to Enter / Space.
Use role="img" for non-interactive Avatars and role="button" for interactive ones.
Do not rely on color (Tone) alone to convey meaning — pair it with a label or tooltip where necessary.
Installation
npm install @xsolla/xui-avatarImports
import {
Avatar,
AvatarGroup,
type AvatarGroupItem,
type AvatarBackgroundMode,
} from "@xsolla/xui-avatar";Quick start
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";
export default function QuickStart() {
return <Avatar text="JD" aria-label="John Doe" />;
}API Reference
<Avatar>
| Prop | Type | Default | Description |
| ------------ | ------------------------------------------------------------------------------------------------------- | --------- | ------------------------------------------------------------------------------------------------------------- |
| testID | string | — | Test ID for testing frameworks. On web this renders as data-testid; on React Native it renders as testID. |
| src | string | — | Image source URL. |
| icon | ReactNode | — | Icon displayed when no src is supplied. |
| text | string | — | Text/initials displayed when no src or icon is supplied. |
| size | "xl" \| "lg" \| "md" \| "sm" \| "xs" \| "xxs" | "xl" | Avatar size. |
| square | boolean | false | Square (small radius) instead of circular. |
| tone | "mono" \| "brand" | "mono" | Visual tone. |
| stroke | boolean | true | Render the avatar's outer border. Set false to remove the border entirely. |
| badge | boolean | false | Show a notification badge. |
| badgeCount | ReactNode | — | Numeric or text content rendered inside the badge. |
| badgeIcon | ReactNode | — | Icon rendered inside the badge. |
| badgeTone | "primary" \| "secondary" \| "brand" \| "brandExtra" \| "success" \| "warning" \| "alert" \| "neutral" | "alert" | Badge colour tone. |
| aria-label | string | — | Accessible label. Recommended for screen readers. |
| alt | string | — | Alt text for the image (falls back to aria-label). |
| onClick | () => void | — | Click handler. When provided, the avatar becomes a focusable button. |
Inherits ThemeOverrideProps (themeMode, themeProductContext).
backgroundColor and disableHover exist on the source type but are reserved for internal use by AvatarGroup (@internal); avoid setting them directly.
<AvatarGroup>
| Prop | Type | Default | Description |
| ---------------------- | ------------------------------------------------------------------------------------------------------------- | --------- | ----------------------------------------------------------------------- |
| list | AvatarGroupItem[] | — | Required. Avatars to display. |
| size | "xxs" \| "xs" \| "sm" \| "md" \| "lg" \| "xl" | "sm" | Size applied to every avatar. |
| maxVisible | number | 6 | Maximum slots rendered, including the "+N" overflow counter. |
| avatarBackgroundMode | "mixed" \| "brand" \| "brandExtra" \| "success" \| "warning" \| "alert" \| "neutral" \| ((theme) => string) | "mixed" | Background colour mode for non-image avatars. |
| aria-label | string | auto | Accessible label for the group. Defaults to "X users" / "N of M users". |
Inherits ThemeOverrideProps (themeMode, themeProductContext).
AvatarGroupItem
interface AvatarGroupItem {
src?: string;
initials?: string;
tooltip?: string;
badge?: boolean;
badgeCount?: React.ReactNode;
badgeIcon?: React.ReactNode;
badgeTone?: AvatarProps["badgeTone"];
}Examples
Sizes
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";
export default function AvatarSizes() {
return (
<div style={{ display: "flex", gap: 16, alignItems: "center" }}>
<Avatar size="xxs" text="A" />
<Avatar size="xs" text="A" />
<Avatar size="sm" text="A" />
<Avatar size="md" text="A" />
<Avatar size="lg" text="A" />
<Avatar size="xl" text="A" />
</div>
);
}Tones
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";
export default function AvatarTones() {
return (
<div style={{ display: "flex", gap: 16 }}>
<Avatar tone="mono" text="JD" />
<Avatar tone="brand" text="JD" />
</div>
);
}Borderless
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";
export default function BorderlessAvatar() {
// stroke defaults to true; pass stroke={false} to remove the outer border
return <Avatar text="JD" stroke={false} />;
}With badge
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";
import { Bell } from "@xsolla/xui-icons-base";
export default function AvatarWithBadge() {
return (
<div style={{ display: "flex", gap: 24 }}>
<Avatar text="JD" badge />
<Avatar text="JD" badge badgeCount={5} />
<Avatar text="JD" badge badgeIcon={<Bell />} badgeTone="brand" />
</div>
);
}Square
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";
export default function SquareAvatar() {
return <Avatar text="AB" square size="lg" />;
}Custom icon
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";
import { Briefcase } from "@xsolla/xui-icons-base";
export default function AvatarWithIcon() {
return <Avatar icon={<Briefcase />} tone="brand" aria-label="Work account" />;
}Clickable
import * as React from "react";
import { Avatar } from "@xsolla/xui-avatar";
export default function ClickableAvatar() {
const [count, setCount] = React.useState(0);
return (
<Avatar
text="JD"
size="lg"
onClick={() => setCount((c) => c + 1)}
aria-label={`Open profile (clicked ${count} times)`}
/>
);
}Avatar group
import * as React from "react";
import { AvatarGroup, type AvatarGroupItem } from "@xsolla/xui-avatar";
export default function AvatarGroupExample() {
const users: AvatarGroupItem[] = [
{ initials: "JD", tooltip: "John Doe" },
{ initials: "AB", tooltip: "Anna Brown" },
{ initials: "CD", tooltip: "Carl Davis" },
{ initials: "EF", tooltip: "Emma Frost" },
{ initials: "GH", tooltip: "Grace Hill" },
{ initials: "IJ", tooltip: "Ivan Jones" },
{ initials: "KL", tooltip: "Kim Lee" },
];
return <AvatarGroup list={users} maxVisible={5} size="md" />;
}Accessibility
- Pass
aria-labelso screen readers can identify the avatar;altfalls back to it for images. - When
onClickis set the root receivesrole="button",tabIndex={0}, and Enter/Space activation. AvatarGroupis arole="group"with an auto-generated label such as "5 of 12 users".- The "+N" overflow counter announces "N more user(s)" via
aria-label.
