Avatar
A visual representation of a user or entity in the product.
import { Avatar } from "@opengovsg/oui"export const Example = () => { return ( <Avatar name="John Doe"> <Avatar.Fallback /> </Avatar> )}Usage
import { Avatar, AvatarGroup } from "@opengovsg/oui"<Avatar name="John Doe">
<Avatar.Image src="https://example.com/avatar.jpg" />
<Avatar.Fallback />
</Avatar>Alternatively, install the component as local source via the shadcn CLI:
npx shadcn@latest add https://oui.open.gov.sg/r/avatar.jsonpnpm dlx shadcn@latest add https://oui.open.gov.sg/r/avatar.jsonnpx shadcn@latest add https://oui.open.gov.sg/r/avatar.jsonbunx --bun shadcn@latest add https://oui.open.gov.sg/r/avatar.jsonOUI exports 2 avatar-related components:
- Avatar: The main component to display an avatar. It has compound components
Avatar.ImageandAvatar.Fallback. - AvatarGroup: A wrapper component to display a group of avatars.
Anatomy
Avatar consists of:
- Avatar — the root container; manages size, color, and fallback state
- Avatar.Image — the
<img>element; triggers fallback when the image fails to load - Avatar.Fallback — rendered when the image is absent or fails; shows initials (from
name) or a default user icon
- Avatar.Image — the
Examples
Sizes
Use the size prop to change the size of the avatar. Available sizes are 2xs, xs, sm, and md.
import { Avatar } from "@opengovsg/oui"export const Example = () => { return ( <div className="flex items-center gap-4"> <Avatar size="2xs" name="2XS"> <Avatar.Fallback /> </Avatar> <Avatar size="xs" name="XS"> <Avatar.Fallback /> </Avatar> <Avatar size="sm" name="SM"> <Avatar.Fallback /> </Avatar> <Avatar size="md" name="MD"> <Avatar.Fallback /> </Avatar> </div> )}Radius
Use the radius prop to change the border radius of the avatar.
import { Avatar } from "@opengovsg/oui"export const Example = () => { return ( <div className="flex items-center gap-4"> <Avatar radius="none" name="None"> <Avatar.Fallback /> </Avatar> <Avatar radius="sm" name="SM"> <Avatar.Fallback /> </Avatar> <Avatar radius="md" name="MD"> <Avatar.Fallback /> </Avatar> <Avatar radius="lg" name="LG"> <Avatar.Fallback /> </Avatar> <Avatar radius="full" name="Full"> <Avatar.Fallback /> </Avatar> </div> )}Colors
Use the prominence and color props to customize the avatar's appearance.
- prominence:
strong(filled background) orsubtle(light background) - color:
primaryorwhite
import { Avatar } from "@opengovsg/oui"export const Example = () => { return ( <div className="flex flex-col gap-4"> <div className="flex items-center gap-4"> <Avatar prominence="strong" color="primary" name="Strong Primary"> <Avatar.Fallback /> </Avatar> <Avatar prominence="subtle" color="primary" name="Subtle Primary"> <Avatar.Fallback /> </Avatar> <Avatar prominence="subtle" color="white" name="Subtle White"> <Avatar.Fallback /> </Avatar> </div> </div> )}Avatar Fallbacks
If the image fails to load, the avatar will display a fallback. There are 2 fallback options:
- If there's a
nameprop, the initials are generated from the name. - If there's no
nameprop, a default user icon is displayed.
import { Avatar } from "@opengovsg/oui"export const Example = () => { return ( <div className="flex items-center gap-4"> {/* Default fallback (user icon) */} <Avatar> <Avatar.Image src="https://invalid-url.com/image.jpg" /> <Avatar.Fallback /> </Avatar> {/* Name-based fallback (initials) */} <Avatar name="John Doe"> <Avatar.Image src="https://invalid-url.com/image.jpg" /> <Avatar.Fallback /> </Avatar> </div> )}Custom Fallback
You can provide a custom fallback component to be displayed when the image fails to load.
import { SettingsIcon } from "lucide-react"import { Avatar } from "@opengovsg/oui"export const Example = () => { return ( <Avatar> <Avatar.Image src="https://invalid-url.com/image.jpg" /> <Avatar.Fallback> <SettingsIcon /> </Avatar.Fallback> </Avatar> )}Custom Initials Logic
You can customize the logic used to generate initials by passing a function to the getInitials prop. By default, we use the first character of each word in the name prop.
<Avatar
name="John Doe"
getInitials={(name) =>
name
.split(" ")
.map((n) => n[0])
.join("")
.toUpperCase()
}
>
<Avatar.Image src="https://example.com/avatar.jpg" />
<Avatar.Fallback />
</Avatar>Avatar Group
Use the AvatarGroup component to display a group of avatars together.
import { Avatar, AvatarGroup } from "@opengovsg/oui"export const Example = () => { return ( <AvatarGroup> <Avatar name="John Doe"> <Avatar.Image src="https://gravatar.com/avatar/193a23ae8a55c4144ae512bb64567c9f?s=400&d=monsterid&r=x" /> <Avatar.Fallback /> </Avatar> <Avatar name="Jane Smith"> <Avatar.Image src="https://gravatar.com/avatar/577cd0a8c4a76de416036642cfb4b53c?s=400&d=monsterid&r=x" /> <Avatar.Fallback /> </Avatar> <Avatar name="Bruce Wayne"> <Avatar.Image src="https://gravatar.com/avatar/746aa139bc568ddbb7a9dd6ee5f98ba0?s=400&d=monsterid&r=x" /> <Avatar.Fallback /> </Avatar> <Avatar name="Bob Wilson"> <Avatar.Image src="https://gravatar.com/avatar/8f62f3c9f639118db002a94e64823d18?s=400&d=monsterid&r=x" /> <Avatar.Fallback /> </Avatar> </AvatarGroup> )}Max Count
You can limit the number of avatars displayed by passing the max prop to the AvatarGroup component.
import { Avatar, AvatarGroup } from "@opengovsg/oui"export const Example = () => { return ( <AvatarGroup max={3}> <Avatar name="John Doe"> <Avatar.Fallback /> </Avatar> <Avatar name="Jane Smith"> <Avatar.Fallback /> </Avatar> <Avatar name="Bruce Wayne"> <Avatar.Fallback /> </Avatar> <Avatar name="Bob Wilson"> <Avatar.Fallback /> </Avatar> </AvatarGroup> )}Total Count
You can display the total number of avatars by passing the total prop to the AvatarGroup component. This is useful when you have more avatars than what you're displaying.
import { Avatar, AvatarGroup } from "@opengovsg/oui"export const Example = () => { return ( <AvatarGroup max={3} total={10}> <Avatar name="John Doe"> <Avatar.Image src="https://gravatar.com/avatar/193a23ae8a55c4144ae512bb64567c9f?s=400&d=monsterid&r=x" /> <Avatar.Fallback /> </Avatar> <Avatar name="Jane Smith"> <Avatar.Image src="https://gravatar.com/avatar/577cd0a8c4a76de416036642cfb4b53c?s=400&d=monsterid&r=x" /> <Avatar.Fallback /> </Avatar> <Avatar name="Bruce Wayne"> <Avatar.Image src="https://gravatar.com/avatar/746aa139bc568ddbb7a9dd6ee5f98ba0?s=400&d=monsterid&r=x" /> <Avatar.Fallback /> </Avatar> <Avatar name="Bob Wilson"> <Avatar.Image src="https://gravatar.com/avatar/8f62f3c9f639118db002a94e64823d18?s=400&d=monsterid&r=x" /> <Avatar.Fallback /> </Avatar> </AvatarGroup> )}Custom Count
AvatarGroup provides a renderCount prop to customize the count displayed.
import { Avatar, AvatarGroup } from "@opengovsg/oui"export const Example = () => { return ( <AvatarGroup max={3} prominence="subtle" total={10} renderCount={(count) => ( <p className="prose-body-1 text-base-content-medium ms-2"> +{count} others </p> )} > <Avatar name="John Doe"> <Avatar.Image src="https://gravatar.com/avatar/6dacc144e568aad3a645388f3635f795?s=400&d=robohash&r=x" /> <Avatar.Fallback /> </Avatar> <Avatar name="Jane Smith"> <Avatar.Image src="https://gravatar.com/avatar/a05a9551ef03ec76331ee9ad60fae0e7?s=400&d=robohash&r=x" /> <Avatar.Fallback /> </Avatar> <Avatar name="Bruce Wayne"> <Avatar.Image src="https://gravatar.com/avatar/193a23ae8a55c4144ae512bb64567c9f?s=400&d=robohash&r=x" /> <Avatar.Fallback /> </Avatar> </AvatarGroup> )}Accessibility
- Avatar is a presentational element; pass
aria-labelor use thenameprop (used asalttext for the image) to identify it to assistive technology. - When used in an
AvatarGroup, provide an accessible label on the group container viaaria-labeloraria-labelledby. - The overflow count avatar is informational; ensure surrounding context conveys the total number of members.
- The
data-loadedattribute on the root element reflects image load state for CSS targeting.
Slots
Slots are named regions of the component you can target with custom Tailwind classes via the classNames prop. Each slot below corresponds to a key on the classNames object.
Avatar slots:
base: Avatar wrapper — includes styles for position and general appearance.image: The<img>element — includes styles for opacity transition and size.fallback: The fallback content container shown when the image fails to load — includes styles for centering.icon: The icon element rendered inside the fallback when nonameis provided.
AvatarGroup slots:
base: The wrapper<div>for the avatar group.count: The overflow count avatar element.
Data Attributes
Avatar has the following attributes on the base element:
- data-loaded: Set when the avatar image has loaded successfully.
Props
Avatar
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | The avatar content (Avatar.Image and Avatar.Fallback) |
name | string | - | The name used for initials generation and image alt text |
size | "2xs" | "xs" | "sm" | "md" | "md" | The size of the avatar |
radius | "none" | "sm" | "md" | "lg" | "full" | "full" | The border radius of the avatar |
prominence | "strong" | "subtle" | "strong" | The visual prominence of the avatar |
color | "primary" | "white" | "primary" | The color scheme of the avatar |
getInitials | (name: string) => string | - | Custom function to generate initials from name |
classNames | SlotsToClasses<AvatarSlots> | - | Custom Tailwind classes for component slots |
className | string | - | Additional class applied to the base element |
refis React's standard ref forwarding to the underlying<span>element; not surfaced as a separate doc-table prop.
Avatar.Image
| Prop | Type | Description |
|---|---|---|
src | string | The image source URL |
... | - | All native <img> attributes |
Avatar.Fallback
| Prop | Type | Description |
|---|---|---|
children | ReactNode | Custom fallback content (defaults to icon/initials) |
AvatarGroup
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | The Avatar components to display |
max | number | 5 | Maximum number of visible avatars before overflow |
total | number | - | Total count used for calculating overflow (useful when not all avatars are rendered) |
renderCount | (count: number) => ReactNode | - | Custom render function for the overflow count element |
countProps | Partial<AvatarProps> | - | Props spread onto the overflow count avatar (e.g., custom classNames or name) |
size | "2xs" | "xs" | "sm" | "md" | - | Size applied to all child avatars |
color | "primary" | "white" | - | Color applied to all child avatars |
prominence | "strong" | "subtle" | - | Prominence applied to all child avatars |
radius | "none" | "sm" | "md" | "lg" | "full" | - | Radius applied to all child avatars |
classNames | SlotsToClasses<"base" | "count"> | - | Custom Tailwind classes for group slots |
Templates
Usage with Menu
You can use the Avatar component as a trigger for a Menu to create a user profile menu.
import { ChevronDown } from "lucide-react"import { Avatar, Button, Menu, MenuItem, MenuSection, MenuTrigger, SubmenuTrigger,} from "@opengovsg/oui"export const Example = () => { return ( <MenuTrigger> <Button variant="clear" className="px-2" endContent={<ChevronDown className="group-pressed:rotate-180" />} > <Avatar size="sm" name="User Name"> <Avatar.Fallback /> </Avatar> </Button> <Menu> <MenuSection title="Actions"> <SubmenuTrigger> <MenuItem id="open">Settings</MenuItem> <Menu> <MenuItem id="open-new">Change Avatar</MenuItem> <MenuItem id="open-current">Update Profile Information</MenuItem> <SubmenuTrigger> <MenuItem id="more">More</MenuItem> <Menu> <MenuItem id="open-email">Change Email Address</MenuItem> <MenuItem id="open-in-alt">Change Password</MenuItem> </Menu> </SubmenuTrigger> </Menu> </SubmenuTrigger> <MenuItem>Logout</MenuItem> </MenuSection> </Menu> </MenuTrigger> )}