Avatar
A shadcn-style avatar component built with Ark UI primitives.
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
const AvatarDemo = () => (
<Avatar>
<AvatarImage
alt="Luke Tracy"
src="https://images.unsplash.com/photo-1543610892-0b1f7e6d8ac1?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>LT</AvatarFallback>
</Avatar>
);
export default AvatarDemo;
Installation
npx shadcn@latest add @ark-cn/avatarInstall the dependency required by this primitive:
npm install @ark-ui/reactCopy the component source into your app:
TSXcomponents/ui/avatar.tsx
"use client";
import { Avatar as AvatarPrimitive } from "@ark-ui/react/avatar";
import { ark } from "@ark-ui/react/factory";
import type { ComponentProps } from "react";
import { cn } from "@/lib/utils";
export const Avatar = ({
className,
radius = "full",
size = "default",
...props
}: AvatarPrimitive.RootProps & {
radius?: "full" | "lg" | "md" | "none";
size?: "default" | "sm" | "lg";
}) => (
<AvatarPrimitive.Root
data-radius={radius}
data-slot="avatar"
data-size={size}
className={cn(
"group/avatar relative flex size-8 shrink-0 overflow-hidden select-none after:absolute after:inset-0 after:border after:border-border after:mix-blend-darken data-[radius=full]:rounded-full data-[radius=full]:after:rounded-full data-[radius=lg]:rounded-lg data-[radius=lg]:after:rounded-lg data-[radius=md]:rounded-md data-[radius=md]:after:rounded-md data-[radius=none]:rounded-none data-[radius=none]:after:rounded-none data-[size=lg]:size-10 data-[size=sm]:size-6 dark:after:mix-blend-lighten",
className,
)}
{...props}
/>
);
export const AvatarImage = ({
className,
...props
}: AvatarPrimitive.ImageProps) => (
<AvatarPrimitive.Image
data-slot="avatar-image"
className={cn("size-full object-cover", className)}
{...props}
/>
);
export const AvatarFallback = ({
className,
...props
}: AvatarPrimitive.FallbackProps) => (
<AvatarPrimitive.Fallback
data-slot="avatar-fallback"
className={cn(
"flex size-full items-center justify-center bg-muted text-sm text-muted-foreground group-data-[radius=full]/avatar:rounded-full group-data-[radius=lg]/avatar:rounded-lg group-data-[radius=md]/avatar:rounded-md group-data-[radius=none]/avatar:rounded-none group-data-[size=sm]/avatar:text-xs",
className,
)}
{...props}
/>
);
export const AvatarBadge = ({
className,
...props
}: ComponentProps<typeof ark.span>) => (
<ark.span
data-slot="avatar-badge"
className={cn(
"absolute right-0 bottom-0 z-10 inline-flex items-center justify-center rounded-full bg-primary text-primary-foreground bg-blend-color ring-2 ring-background select-none",
"group-data-[size=sm]/avatar:size-2 group-data-[size=sm]/avatar:[&>svg]:hidden",
"group-data-[size=default]/avatar:size-2.5 group-data-[size=default]/avatar:[&>svg]:size-2",
"group-data-[size=lg]/avatar:size-3 group-data-[size=lg]/avatar:[&>svg]:size-2",
className,
)}
{...props}
/>
);
export const AvatarGroup = ({
className,
...props
}: ComponentProps<typeof ark.div>) => (
<ark.div
data-slot="avatar-group"
className={cn(
"group/avatar-group flex -space-x-2 *:data-[slot=avatar]:ring-2 *:data-[slot=avatar]:ring-background",
className,
)}
{...props}
/>
);
export const AvatarGroupCount = ({
className,
...props
}: ComponentProps<typeof ark.div>) => (
<ark.div
data-slot="avatar-group-count"
className={cn(
"relative flex size-8 shrink-0 items-center justify-center rounded-full bg-muted text-sm text-muted-foreground ring-2 ring-background group-has-data-[size=lg]/avatar-group:size-10 group-has-data-[size=sm]/avatar-group:size-6 [&>svg]:size-4 group-has-data-[size=lg]/avatar-group:[&>svg]:size-5 group-has-data-[size=sm]/avatar-group:[&>svg]:size-3",
className,
)}
{...props}
/>
);
Update import aliases to match your project setup.
Usage
import * as Avatar from "@/components/ui/avatar"Read exported parts in src/components/ui/avatar.tsx and compose the primitive according to the Ark UI pattern for this component.
Examples
Main demo
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
const AvatarDemo = () => (
<Avatar>
<AvatarImage
alt="Luke Tracy"
src="https://images.unsplash.com/photo-1543610892-0b1f7e6d8ac1?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>LT</AvatarFallback>
</Avatar>
);
export default AvatarDemo;
Fallback Only
CN
import { Avatar, AvatarFallback } from "@/components/ui/avatar";
const AvatarFallbackOnlyDemo = () => (
<Avatar>
<AvatarFallback>CN</AvatarFallback>
</Avatar>
);
export default AvatarFallbackOnlyDemo;
Different Sizes
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
const AvatarDifferentSizesDemo = () => (
<div className="flex items-center gap-3">
<Avatar size="sm">
<AvatarImage
alt="Small avatar"
src="https://images.unsplash.com/photo-1543610892-0b1f7e6d8ac1?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>SM</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage
alt="Default avatar"
src="https://images.unsplash.com/photo-1543610892-0b1f7e6d8ac1?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>DF</AvatarFallback>
</Avatar>
<Avatar size="lg">
<AvatarImage
alt="Large avatar"
src="https://images.unsplash.com/photo-1543610892-0b1f7e6d8ac1?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>LG</AvatarFallback>
</Avatar>
</div>
);
export default AvatarDifferentSizesDemo;
Different Radius
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
const AvatarDifferentRadiusDemo = () => (
<div className="flex items-center gap-3">
<Avatar radius="full">
<AvatarImage
alt="Round avatar"
src="https://images.unsplash.com/photo-1543610892-0b1f7e6d8ac1?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>RF</AvatarFallback>
</Avatar>
<Avatar radius="lg">
<AvatarImage
alt="Large radius avatar"
src="https://images.unsplash.com/photo-1543610892-0b1f7e6d8ac1?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>RL</AvatarFallback>
</Avatar>
<Avatar radius="md">
<AvatarImage
alt="Medium radius avatar"
src="https://images.unsplash.com/photo-1543610892-0b1f7e6d8ac1?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>RM</AvatarFallback>
</Avatar>
<Avatar radius="none">
<AvatarImage
alt="Square avatar"
src="https://images.unsplash.com/photo-1543610892-0b1f7e6d8ac1?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>RN</AvatarFallback>
</Avatar>
</div>
);
export default AvatarDifferentRadiusDemo;
Group Avatars
+4
import {
Avatar,
AvatarFallback,
AvatarGroup,
AvatarGroupCount,
AvatarImage,
} from "@/components/ui/avatar";
const AvatarGroupDemo = () => (
<AvatarGroup>
<Avatar>
<AvatarImage
alt="Ari Cole"
src="https://images.unsplash.com/photo-1438761681033-6461ffad8d80?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>AC</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage
alt="Mika Tran"
src="https://images.unsplash.com/photo-1500648767791-00dcc994a43e?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>MT</AvatarFallback>
</Avatar>
<Avatar>
<AvatarImage
alt="Noah Kim"
src="https://images.unsplash.com/photo-1502685104226-ee32379fefbe?w=128&h=128&dpr=2&q=80"
/>
<AvatarFallback>NK</AvatarFallback>
</Avatar>
<AvatarGroupCount>+4</AvatarGroupCount>
</AvatarGroup>
);
export default AvatarGroupDemo;
API reference
This component mirrors the upstream Ark UI primitive.
See the ARK UI documentation for the full API.