Avatar
Initials in a toned circle, plus a group that overlaps them.
betaAvatar.tsx
Examples
Sizes
avatar/sizes.tsx
import { Avatar } from "@streamui/react";
export default function AvatarSizes() {
return (
<div className="flex items-center gap-4">
<Avatar initials="AL" name="Ada Lovelace" size="sm" tone="pink" />
<Avatar initials="GH" name="Grace Hopper" size="md" tone="lime" />
<Avatar initials="KJ" name="Katherine Johnson" size="lg" tone="blue" />
</div>
);
}Group
avatar/group.tsx
import { Avatar, AvatarGroup } from "@streamui/react";
const team = [
{ initials: "AL", name: "Ada Lovelace", tone: "lime" },
{ initials: "GH", name: "Grace Hopper", tone: "pink" },
{ initials: "KJ", name: "Katherine Johnson", tone: "blue" },
{ initials: "MH", name: "Margaret Hamilton", tone: "violet" },
] as const;
export default function AvatarGroupExample() {
return (
<AvatarGroup aria-label="Project members">
{team.map((m) => (
<Avatar key={m.initials} initials={m.initials} name={m.name} tone={m.tone} />
))}
<Avatar initials="+6" name="6 more members" tone="paper" />
</AvatarGroup>
);
}Installation
bun add @streamui/reactusage.tsx
import { Avatar, AvatarGroup } from "@streamui/react";
<Avatar initials="AL" name="Ada Lovelace" />API
| Prop | Type | Default | Description |
|---|---|---|---|
| initials | string | — | One or two characters. |
| name | string | — | Full name, announced instead of the initials. |
| tone | Tone | "paper" | "lime" | Fill color. |
| size | "sm" | "md" | "lg" | "md" | Diameter. |
| Prop | Type | Default | Description |
|---|---|---|---|
| ...props | ComponentProps<"div"> | — | Renders role="group"; give it an aria-label. |
Accessibility
- With a name, the avatar is role="img" and reads the full name instead of the initials.
- AvatarGroup is a role="group", so an aria-label describes the whole stack.
Source
packages/react/src/Avatar.tsx
import type { ComponentProps } from "react";
import { cn } from "./cn";
import { toneBg, type Tone } from "./tones";
export type AvatarSize = "sm" | "md" | "lg";
const sizes: Record<AvatarSize, string> = {
sm: "size-9 text-xs",
md: "size-12 text-sm",
lg: "size-16 text-base",
};
export type AvatarProps = ComponentProps<"span"> & {
/** One or two characters, e.g. "AK". */
initials: string;
/** Full name, announced to screen readers. */
name?: string;
tone?: Tone | "paper";
size?: AvatarSize;
};
export function Avatar({
initials,
name,
tone = "lime",
size = "md",
className,
...props
}: AvatarProps) {
return (
<span
role={name ? "img" : undefined}
aria-label={name}
className={cn(
"grid shrink-0 place-items-center rounded-full border-2 border-ink font-mono font-bold",
tone === "paper" ? "bg-paper-raised text-ink" : cn(toneBg[tone], "text-on-bright"),
sizes[size],
className,
)}
{...props}
>
<span aria-hidden={name ? true : undefined}>{initials}</span>
</span>
);
}
/** Overlaps its children like a stack of coins. */
export function AvatarGroup({ className, ...props }: ComponentProps<"div">) {
return <div role="group" className={cn("flex items-center [&>*+*]:-ml-3", className)} {...props} />;
}