ComponentsAvatar
Avatar
Represent a person or team with an image or initials.
On this page
Use this component from the local workspace package. Follow the setup guide first. A public npm package and registry are not available yet.
View registry filesImport the component into your page or component file.
import { Avatar } from "@dethink/components";
export function Example() {
return <Avatar name="Noah Reyes" src="/avatars/noah.png" ring="border" />;
}Try the examples, then open the code to use them in your app.
Identity rows
Use a real image when available, then fall back to generated initials without changing the surrounding layout.
Noah Reyes
OwnerDesign systems
Mira Patel
ActiveData operations
June Okafor
FallbackIdentity falls back to initials
Show sourceexamples/avatar/basic.tsx
"use client";
import { Avatar, Badge } from "@dethink/components";
import { avatarImagePaths } from "@/examples/_shared/avatar-members";
const people = [
{
image: avatarImagePaths.noah,
meta: "Design systems",
name: "Noah Reyes",
status: "Owner",
tone: "primary" as const,
},
{
image: avatarImagePaths.mira,
meta: "Data operations",
name: "Mira Patel",
status: "Active",
tone: "success" as const,
},
{
image: undefined,
meta: "Identity falls back to initials",
name: "June Okafor",
status: "Fallback",
tone: "info" as const,
},
];
export function AvatarBasic() {
return (
<div className="grid w-full max-w-md gap-4">
{people.map((person) => (
<div
key={person.name}
className="flex min-w-0 items-center gap-[var(--dt-space-3)]"
>
<Avatar
name={person.name}
src={person.image}
tone={person.tone}
ring="border"
size="lg"
decoding="async"
loading="lazy"
sizes="48px"
/>
<div className="min-w-0 flex-1">
<div className="flex min-w-0 flex-wrap items-center gap-2">
<p className="truncate text-sm font-medium">{person.name}</p>
<Badge size="xs" tone={person.tone}>
{person.status}
</Badge>
</div>
<p className="text-muted-foreground truncate text-xs">
{person.meta}
</p>
</div>
</div>
))}
</div>
);
}Sizes, shapes, and rings
Size follows density tokens, shape controls clipping, and ring adds separation on layered surfaces.
Show sourceexamples/avatar/sizes.tsx
"use client";
import {
Avatar,
type AvatarRing,
type AvatarShape,
type AvatarSize,
} from "@dethink/components";
import { avatarImagePaths } from "@/examples/_shared/avatar-members";
const sizes: AvatarSize[] = ["xs", "sm", "md", "lg", "xl", "2xl"];
const shapes: AvatarShape[] = ["circle", "rounded", "square"];
const rings: AvatarRing[] = ["none", "border", "ring"];
export function AvatarSizes() {
return (
<div className="grid w-full gap-8">
<div className="flex flex-wrap items-end gap-[var(--dt-space-4)]">
{sizes.map((size) => (
<div key={size} className="grid justify-items-center gap-2">
<Avatar
name={`Avatar size ${size}`}
src={avatarImagePaths.eli}
size={size}
ring="border"
decoding="async"
loading="lazy"
sizes="72px"
/>
<span className="text-muted-foreground text-xs">{size}</span>
</div>
))}
</div>
<div className="grid gap-4 sm:grid-cols-3">
{shapes.map((shape) => (
<div
key={shape}
className="flex min-w-0 items-center gap-[var(--dt-space-3)]"
>
<Avatar
name={`${shape} avatar`}
src={avatarImagePaths.mira}
shape={shape}
size="lg"
ring="border"
decoding="async"
loading="lazy"
sizes="48px"
/>
<span className="truncate text-sm font-medium capitalize">
{shape}
</span>
</div>
))}
</div>
<div className="flex flex-wrap items-center gap-[var(--dt-space-4)]">
{rings.map((ring) => (
<div key={ring} className="grid justify-items-center gap-2">
<Avatar
name={`${ring} ring avatar`}
src={avatarImagePaths.leo}
ring={ring}
size="xl"
decoding="async"
loading="lazy"
sizes="64px"
/>
<span className="text-muted-foreground text-xs">{ring}</span>
</div>
))}
</div>
</div>
);
}Fallback states
Avatar handles loaded images, generated initials, icon identities, organization shapes, and failed image sources.
Image loaded
Accessible image alt
Generated initials
Name-derived fallback
Icon fallback
System identity
Organization
Square brand shape
Failed image fallback
onErrorThe image source intentionally fails and returns to fallback content.
Show sourceexamples/avatar/states.tsx
"use client";
import { Building2, Bot, UserRound } from "lucide-react";
import { Avatar, Badge } from "@dethink/components";
import { avatarImagePaths } from "@/examples/_shared/avatar-members";
export function AvatarStates() {
return (
<div className="grid w-full gap-4 sm:grid-cols-2">
<div className="flex items-center gap-[var(--dt-space-3)]">
<Avatar
name="Mira Patel"
src={avatarImagePaths.mira}
ring="ring"
size="lg"
decoding="async"
loading="lazy"
sizes="48px"
/>
<div>
<p className="text-sm font-medium">Image loaded</p>
<p className="text-muted-foreground text-xs">Accessible image alt</p>
</div>
</div>
<div className="flex items-center gap-[var(--dt-space-3)]">
<Avatar name="Sam Rivera" tone="warning" ring="border" size="lg" />
<div>
<p className="text-sm font-medium">Generated initials</p>
<p className="text-muted-foreground text-xs">Name-derived fallback</p>
</div>
</div>
<div className="flex items-center gap-[var(--dt-space-3)]">
<Avatar
fallbackIcon={<Bot />}
name="AI reviewer"
tone="info"
shape="rounded"
ring="border"
size="lg"
/>
<div>
<p className="text-sm font-medium">Icon fallback</p>
<p className="text-muted-foreground text-xs">System identity</p>
</div>
</div>
<div className="flex items-center gap-[var(--dt-space-3)]">
<Avatar
fallbackIcon={<Building2 />}
name="Northstar Audit"
shape="square"
tone="success"
ring="border"
size="lg"
/>
<div>
<p className="text-sm font-medium">Organization</p>
<p className="text-muted-foreground text-xs">Square brand shape</p>
</div>
</div>
<div className="flex items-center gap-[var(--dt-space-3)] sm:col-span-2">
<Avatar
fallbackIcon={<UserRound />}
name="Missing profile"
src="/avatars/showcase/missing-profile.png"
loading="lazy"
tone="destructive"
ring="border"
size="lg"
/>
<div className="min-w-0">
<div className="flex flex-wrap items-center gap-2">
<p className="text-sm font-medium">Failed image fallback</p>
<Badge size="xs" tone="destructive" variant="outline">
onError
</Badge>
</div>
<p className="text-muted-foreground text-xs">
The image source intentionally fails and returns to fallback
content.
</p>
</div>
</div>
</div>
);
}Profile list recipe
A production row pairs Avatar with Badge and recency metadata for review and access workflows.
Show sourceexamples/avatar/recipe-profile-row.tsx
"use client";
import { Clock3, ShieldCheck } from "lucide-react";
import {
Avatar,
Badge,
Card,
CardContent,
CardDescription,
CardHeader,
CardTitle,
} from "@dethink/components";
import { avatarImagePaths } from "@/examples/_shared/avatar-members";
const reviewers = [
{
image: avatarImagePaths.noah,
lastSeen: "2 min ago",
name: "Noah Reyes",
role: "Design systems",
tone: "primary" as const,
},
{
image: avatarImagePaths.mira,
lastSeen: "15 min ago",
name: "Mira Patel",
role: "Release operations",
tone: "success" as const,
},
{
image: avatarImagePaths.leo,
lastSeen: "1 hr ago",
name: "Leo Novak",
role: "Product lead",
tone: "warning" as const,
},
];
export function AvatarProfileRow() {
return (
<Card className="w-full max-w-lg">
<CardHeader>
<CardTitle>Access review</CardTitle>
<CardDescription>
Identity rows pair avatars with status metadata and compact badges.
</CardDescription>
</CardHeader>
<CardContent className="grid gap-4">
{reviewers.map((reviewer) => (
<div
key={reviewer.name}
className="flex min-w-0 items-center justify-between gap-[var(--dt-space-4)]"
>
<div className="flex min-w-0 items-center gap-[var(--dt-space-3)]">
<Avatar
name={reviewer.name}
src={reviewer.image}
tone={reviewer.tone}
ring="border"
size="lg"
decoding="async"
loading="lazy"
sizes="48px"
/>
<div className="min-w-0">
<p className="truncate text-sm font-medium">{reviewer.name}</p>
<p className="text-muted-foreground truncate text-xs">
{reviewer.role}
</p>
</div>
</div>
<div className="flex shrink-0 flex-col items-end gap-1">
<Badge icon={<ShieldCheck />} size="xs" tone={reviewer.tone}>
Verified
</Badge>
<span className="text-muted-foreground inline-flex items-center gap-1 text-xs">
<Clock3 aria-hidden="true" className="size-3" />
{reviewer.lastSeen}
</span>
</div>
</div>
))}
</CardContent>
</Card>
);
}AvatarProps extends span attributes while owning image accessibility and layout-sensitive image props.
| Prop | What it does | Default |
|---|---|---|
namestring | Accessible label and source for generated initials when explicit initials are not provided. | Not set |
srcstring | Image URL rendered inside the avatar until it fails, then fallback content is shown. | Not set |
initialsstring | Explicit fallback initials, normalized to at most three uppercase characters. | Not set |
fallbackIconReactNode | Icon rendered when no image or initials are available, useful for teams and systems. | Not set |
size"xs" | "sm" | "md" | "lg" | "xl" | "2xl" | Density-aware avatar size. | "md" |
shape"circle" | "rounded" | "square" | Avatar clipping shape. | "circle" |
tone"neutral" | "primary" | "success" | "warning" | "destructive" | "info" | Fallback background and foreground tone. | "neutral" |
ring"none" | "border" | "ring" | Optional edge treatment for separation on busy surfaces. | "none" |
motion"none" | "subtle" | "standard" | Hover and focus feedback, reduced automatically for users who prefer less motion. | "standard" |
decorativeboolean | Marks the avatar as presentation-only when surrounding text already provides the identity. | false |
imagePropsAvatarImageProps | Safe pass-through props for the internal image, excluding layout and accessibility props owned by Avatar. | Not set |