Components

Avatar Group

A component that arranges avatars with overlapping visual effects for displaying multiple users or items.

API
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { AvatarGroup } from "@/components/ui/avatar-group";
 
const avatars = [
  {
    name: "shadcn",
    src: "https://github.com/shadcn.png",
    fallback: "CN",
  },
  {
    name: "Ethan Niser",
    src: "https://github.com/ethanniser.png",
    fallback: "EN",
  },
  {
    name: "Guillermo Rauch",
    src: "https://github.com/rauchg.png",
    fallback: "GR",
  },
  {
    name: "Lee Robinson",
    src: "https://github.com/leerob.png",
    fallback: "LR",
  },
  {
    name: "Evil Rabbit",
    src: "https://github.com/evilrabbit.png",
    fallback: "ER",
  },
  {
    name: "Tim Neutkens",
    src: "https://github.com/timneutkens.png",
    fallback: "TN",
  },
];
 
export function AvatarGroupDemo() {
  return (
    <div className="flex flex-col gap-8">
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Avatar Group</h3>
        <AvatarGroup>
          {avatars.slice(0, 4).map((avatar, index) => (
            <Avatar key={index}>
              <AvatarImage src={avatar.src} alt={avatar.name} />
              <AvatarFallback>{avatar.fallback}</AvatarFallback>
            </Avatar>
          ))}
        </AvatarGroup>
      </div>
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">
          Avatar Group with overflow (max 4)
        </h3>
        <AvatarGroup max={4}>
          {avatars.map((avatar, index) => (
            <Avatar key={index}>
              <AvatarImage src={avatar.src} alt={avatar.name} />
              <AvatarFallback>{avatar.fallback}</AvatarFallback>
            </Avatar>
          ))}
        </AvatarGroup>
      </div>
    </div>
  );
}

Installation

CLI

npx shadcn@latest add "https://jolyui.dev/r/avatar-group"

Manual

Install the following dependencies:

npm install @radix-ui/react-slot class-variance-authority

Copy and paste the following code into your project.

import { cn } from "@/lib/utils";
import {
  AvatarGroup as AvatarGroupPrimitive,
  type AvatarGroupProps,
} from "@jolyui/avatar-group";
 
// Re-export with cn utility integrated
export function AvatarGroup({
  className,
  ...props
}: React.ComponentProps<typeof AvatarGroupPrimitive>) {
  return <AvatarGroupPrimitive className={cn(className)} {...props} />;
}
 
export type { AvatarGroupProps };

Layout

import { AvatarGroup } from "@/components/ui/avatar-group";
import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";

<AvatarGroup>
  <Avatar>
    <AvatarImage src="/tony-hawk.png" />
    <AvatarFallback>TH</AvatarFallback>
  </Avatar>
  <Avatar>
    <AvatarImage src="/rodney-mullen.png" />
    <AvatarFallback>RM</AvatarFallback>
  </Avatar>
</AvatarGroup>

Examples

With Truncation

Automatically truncate long lists and show overflow indicators with the max prop.

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { AvatarGroup } from "@/components/ui/avatar-group";
 
const avatars = [
  {
    name: "shadcn",
    src: "https://github.com/shadcn.png",
    fallback: "CN",
  },
  {
    name: "Ethan Niser",
    src: "https://github.com/ethanniser.png",
    fallback: "EN",
  },
  {
    name: "Guillermo Rauch",
    src: "https://github.com/rauchg.png",
    fallback: "GR",
  },
  {
    name: "Lee Robinson",
    src: "https://github.com/leerob.png",
    fallback: "LR",
  },
  {
    name: "Evil Rabbit",
    src: "https://github.com/evilrabbit.png",
    fallback: "ER",
  },
  {
    name: "Tim Neutkens",
    src: "https://github.com/timneutkens.png",
    fallback: "TN",
  },
  {
    name: "Delba de Oliveira",
    src: "https://github.com/delbaoliveira.png",
    fallback: "DO",
  },
  {
    name: "Shu Ding",
    src: "https://github.com/shuding.png",
    fallback: "SD",
  },
];
 
export function AvatarGroupTruncationDemo() {
  return (
    <div className="flex flex-col gap-6">
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Max 3 items</h3>
        <AvatarGroup max={3}>
          {avatars.map((avatar, index) => (
            <Avatar key={index}>
              <AvatarImage src={avatar.src} alt={avatar.name} />
              <AvatarFallback>{avatar.fallback}</AvatarFallback>
            </Avatar>
          ))}
        </AvatarGroup>
      </div>
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Max 5 items</h3>
        <AvatarGroup max={5}>
          {avatars.map((avatar, index) => (
            <Avatar key={index}>
              <AvatarImage src={avatar.src} alt={avatar.name} />
              <AvatarFallback>{avatar.fallback}</AvatarFallback>
            </Avatar>
          ))}
        </AvatarGroup>
      </div>
    </div>
  );
}

With RTL

Support for right-to-left layouts and vertical RTL stacking.

import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";
import { AvatarGroup } from "@/components/ui/avatar-group";
 
const avatars = [
  {
    name: "shadcn",
    src: "https://github.com/shadcn.png",
    fallback: "CN",
  },
  {
    name: "Ethan Niser",
    src: "https://github.com/ethanniser.png",
    fallback: "EN",
  },
  {
    name: "Guillermo Rauch",
    src: "https://github.com/rauchg.png",
    fallback: "GR",
  },
  {
    name: "Lee Robinson",
    src: "https://github.com/leerob.png",
    fallback: "LR",
  },
];
 
export function AvatarGroupRtlDemo() {
  return (
    <div className="grid gap-6 md:grid-cols-2">
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">RTL</h3>
        <AvatarGroup dir="rtl">
          {avatars.map((avatar, index) => (
            <Avatar key={index}>
              <AvatarImage src={avatar.src} alt={avatar.name} />
              <AvatarFallback>{avatar.fallback}</AvatarFallback>
            </Avatar>
          ))}
        </AvatarGroup>
      </div>
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Reverse RTL</h3>
        <AvatarGroup dir="rtl" reverse>
          {avatars.map((avatar, index) => (
            <Avatar key={index}>
              <AvatarImage src={avatar.src} alt={avatar.name} />
              <AvatarFallback>{avatar.fallback}</AvatarFallback>
            </Avatar>
          ))}
        </AvatarGroup>
      </div>
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Vertical RTL</h3>
        <div className="flex justify-center">
          <AvatarGroup orientation="vertical" dir="rtl">
            {avatars.map((avatar, index) => (
              <Avatar key={index}>
                <AvatarImage src={avatar.src} alt={avatar.name} />
                <AvatarFallback>{avatar.fallback}</AvatarFallback>
              </Avatar>
            ))}
          </AvatarGroup>
        </div>
      </div>
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Vertical reverse RTL</h3>
        <div className="flex justify-center">
          <AvatarGroup orientation="vertical" dir="rtl" reverse>
            {avatars.map((avatar, index) => (
              <Avatar key={index}>
                <AvatarImage src={avatar.src} alt={avatar.name} />
                <AvatarFallback>{avatar.fallback}</AvatarFallback>
              </Avatar>
            ))}
          </AvatarGroup>
        </div>
      </div>
    </div>
  );
}

With Icons

Use the Avatar Group component with icons or other elements beyond avatars.

import { Bell, Heart, MessageCircle, Settings, Star, User } from "lucide-react";
import { AvatarGroup } from "@/components/ui/avatar-group";
 
const iconData = [
  { icon: User, color: "bg-blue-500" },
  { icon: Heart, color: "bg-red-500" },
  { icon: Star, color: "bg-yellow-500" },
  { icon: MessageCircle, color: "bg-green-500" },
  { icon: Settings, color: "bg-purple-500" },
  { icon: Bell, color: "bg-orange-500" },
];
 
export function AvatarGroupIconsDemo() {
  return (
    <div className="grid grid-cols-1 gap-6 md:grid-cols-2">
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Icon Group</h3>
        <AvatarGroup>
          {iconData.slice(0, 4).map((item, index) => {
            const IconComponent = item.icon;
            return (
              <div
                key={index}
                className={`flex size-10 items-center justify-center rounded-full text-white ${item.color}`}
              >
                <IconComponent size={16} />
              </div>
            );
          })}
        </AvatarGroup>
      </div>
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Icon Group with Truncation</h3>
        <AvatarGroup max={3}>
          {iconData.map((item, index) => {
            const IconComponent = item.icon;
            return (
              <div
                key={index}
                className={`flex size-10 items-center justify-center rounded-full text-white ${item.color}`}
              >
                <IconComponent size={16} />
              </div>
            );
          })}
        </AvatarGroup>
      </div>
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Reverse Icon Group</h3>
        <AvatarGroup reverse>
          {iconData.slice(0, 4).map((item, index) => {
            const IconComponent = item.icon;
            return (
              <div
                key={index}
                className={`flex size-10 items-center justify-center rounded-full text-white ${item.color}`}
              >
                <IconComponent size={16} />
              </div>
            );
          })}
        </AvatarGroup>
      </div>
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Reverse with Truncation</h3>
        <AvatarGroup reverse max={3}>
          {iconData.map((item, index) => {
            const IconComponent = item.icon;
            return (
              <div
                key={index}
                className={`flex size-10 items-center justify-center rounded-full text-white ${item.color}`}
              >
                <IconComponent size={16} />
              </div>
            );
          })}
        </AvatarGroup>
      </div>
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Vertical Icon Group</h3>
        <div className="flex justify-center">
          <AvatarGroup orientation="vertical" size={32}>
            {iconData.slice(0, 4).map((item, index) => {
              const IconComponent = item.icon;
              return (
                <div
                  key={index}
                  className={`flex size-8 items-center justify-center rounded-full text-white ${item.color}`}
                >
                  <IconComponent size={14} />
                </div>
              );
            })}
          </AvatarGroup>
        </div>
      </div>
      <div className="flex flex-col gap-3">
        <h3 className="font-medium text-sm">Vertical Reverse Icon Group</h3>
        <div className="flex justify-center">
          <AvatarGroup orientation="vertical" reverse size={32}>
            {iconData.slice(0, 4).map((item, index) => {
              const IconComponent = item.icon;
              return (
                <div
                  key={index}
                  className={`flex size-8 items-center justify-center rounded-full text-white ${item.color}`}
                >
                  <IconComponent size={14} />
                </div>
              );
            })}
          </AvatarGroup>
        </div>
      </div>
    </div>
  );
}

API Reference

AvatarGroup

The main avatar group container that handles layout and masking of child elements.

Prop

Type

Data AttributeValue
[data-orientation]"horizontal" | "vertical"

How is this guide?