Oblivion
Oblivion
Docs
Go back
Component by
OV
Oblivion
Person 1
Person 2
Person 3
Person 4
+2
TSX
avatar-stack
"use client"

import Image from "next/image"
import { cn } from "@/lib/utils"

export const componentMeta = {
  name: "Avatar Stack",
  description: "Overlapping circular avatars with an overflow count badge. Great for social proof sections.",
  props: {
    avatars:  { type: "Array<{ src: string; alt: string }>", description: "Avatar image objects" },
    max:      { type: "number", default: "4",   description: "Maximum avatars shown before +N badge" },
    size:     { type: "'sm' | 'md' | 'lg'",     default: '"md"', description: "Avatar size" },
    label:    { type: "string",                 description: "Optional label displayed next to the stack" },
  }
}

type Size = "sm" | "md" | "lg"
const SIZE_CLS: Record<Size, string> = {
  sm: "w-7 h-7 text-[10px]",
  md: "w-9 h-9 text-xs",
  lg: "w-12 h-12 text-sm",
}
const OFFSET_CLS: Record<Size, string> = {
  sm: "-ml-2",
  md: "-ml-2.5",
  lg: "-ml-3",
}

// Unsplash License — free for commercial use, no attribution required
const DEFAULT_AVATARS = [
  { src: "https://images.unsplash.com/photo-1544005313-94ddf0286df2?w=80&h=80&fit=crop&crop=face", alt: "Person 1" },
  { src: "https://images.unsplash.com/photo-1506794778202-cad84cf45f1d?w=80&h=80&fit=crop&crop=face", alt: "Person 2" },
  { src: "https://images.unsplash.com/photo-1531746020798-e6953c6e8e04?w=80&h=80&fit=crop&crop=face", alt: "Person 3" },
  { src: "https://images.unsplash.com/photo-1499996860823-5214fcc65f8f?w=80&h=80&fit=crop&crop=face", alt: "Person 4" },
  { src: "https://images.unsplash.com/photo-1488426862026-3ee34a7d66df?w=80&h=80&fit=crop&crop=face", alt: "Person 5" },
  { src: "https://images.unsplash.com/photo-1463453091185-61582044d556?w=80&h=80&fit=crop&crop=face", alt: "Person 6" },
]

export default function AvatarStack({
  avatars = DEFAULT_AVATARS,
  max     = 4,
  size    = "md",
  label,
  className,
}: {
  avatars?:  { src: string; alt: string }[]
  max?:      number
  size?:     Size
  label?:    string
  className?: string
}) {
  const shown    = avatars.slice(0, max)
  const overflow = avatars.length - max

  return (
    <div className={cn("flex items-center gap-3", className)}>
      <div className="flex items-center">
        {shown.map((av, i) => (
          <div
            key={i}
            className={cn(
              "relative rounded-full ring-2 ring-white dark:ring-zinc-950 overflow-hidden shrink-0",
              SIZE_CLS[size],
              i > 0 && OFFSET_CLS[size]
            )}
          >
            <Image
              src={av.src}
              alt={av.alt}
              fill
              className="object-cover"
              sizes="80px"
              unoptimized
            />
          </div>
        ))}

        {overflow > 0 && (
          <div
            className={cn(
              "flex items-center justify-center rounded-full ring-2 ring-white dark:ring-zinc-950",
              "bg-zinc-100 dark:bg-zinc-800 text-zinc-600 dark:text-zinc-400 font-semibold",
              SIZE_CLS[size],
              OFFSET_CLS[size]
            )}
          >
            +{overflow}
          </div>
        )}
      </div>

      {label && (
        <span className="text-sm text-zinc-600 dark:text-zinc-400 font-medium">{label}</span>
      )}
    </div>
  )
}

export function AvatarStackPreview() {
  return (
    <div className="flex h-full min-h-[480px] w-full flex-col items-center justify-center gap-10 bg-transparent">
      <div className="text-center space-y-1">
        <p className="text-xs font-semibold uppercase tracking-widest text-zinc-500 dark:text-zinc-400">Trusted by</p>
        <p className="text-2xl font-bold text-zinc-900 dark:text-white">12,000+ developers</p>
      </div>
      <div className="flex flex-col items-center gap-6">
        <AvatarStack size="lg" label="12k+ developers trust Oblivion UI" />
        <AvatarStack size="md" max={3} label="Join the community" />
        <AvatarStack size="sm" max={5} />
      </div>
    </div>
  )
}
Save to favoritesCopy to Figma
React

Avatar Stack

Overlapping circular avatars with an overflow count badge. Great for social proof sections.

Installation

$ npx @cosmoo/oblivion add avatar-stack
Dependenciesnpm install

Usage Example

tsx
import AvatarStack from "@/components/oblivion/avatar-stack"

export default function Demo() {
  return (
    <div className="p-10 flex justify-center">
      <AvatarStack />
    </div>
  )
}