Avatar Group
scrollxui/avatar-groupFreeComponent
Overlapping avatar circles that expand smoothly to reveal each user's name on hover.
Live preview
live · rendered by the registry
Rendered by scrollxui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://scrollxui.dev/registry/avatar-group.jsonSource
1'use client';2import { useState, useEffect, useRef } from 'react';3import { cn } from '@/lib/utils';45interface Avatar {6 imageUrl: string;7 name: string;8}910interface AvatarGroupProps {11 className?: string;12 avatarUrls: Avatar[];13}1415const AvatarGroup = ({ className, avatarUrls }: AvatarGroupProps) => {16 const [hoveredIndex, setHoveredIndex] = useState<number | null>(null);17 const containerRef = useRef<HTMLDivElement>(null);1819 useEffect(() => {20 const handleClickOutside = (event: MouseEvent | TouchEvent) => {21 if (22 containerRef.current &&23 !containerRef.current.contains(event.target as Node)24 ) {25 setHoveredIndex(null);26 }27 };2829 document.addEventListener('mousedown', handleClickOutside);30 document.addEventListener('touchstart', handleClickOutside);3132 return () => {33 document.removeEventListener('mousedown', handleClickOutside);34 document.removeEventListener('touchstart', handleClickOutside);35 };36 }, []);3738 const handleInteraction = (39 index: number,40 e: React.MouseEvent | React.TouchEvent,41 ) => {42 e.preventDefault();43 setHoveredIndex(hoveredIndex === index ? null : index);44 };4546 return (47 <div48 ref={containerRef}49 className={cn('z-10 flex -space-x-4 rtl:space-x-reverse', className)}50 >51 {avatarUrls.map((avatar, index) => {52 const isHovered = hoveredIndex === index;53 const showName = isHovered;5455 return (56 <div57 key={index}58 onMouseEnter={() => setHoveredIndex(index)}59 onMouseLeave={() => setHoveredIndex(null)}60 onTouchEnd={(e) => handleInteraction(index, e)}61 onClick={(e) => handleInteraction(index, e)}62 className={cn(63 'relative flex items-center gap-0 rounded-full cursor-pointer touch-none',64 'transition-all duration-500 ease-out',65 ' hover:z-10',66 showName ? 'pr-6 pl-2 py-2' : 'p-0.5',67 )}68 >69 <div className='relative shrink-0'>70 <img71 src={avatar.imageUrl}72 alt={avatar.name}73 width={40}74 height={40}75 className={cn(76 'h-10 w-10 rounded-full object-cover border-2 border-white dark:border-gray-800',77 'transition-all duration-500 ease-out',78 'hover:scale-105',79 )}80 />81 </div>8283 <div84// … truncated
What it pulls in
npm packages
Other registry items
Files it writes
More from scrollxui
All 180 items| Component | Registry | Kind | Access | Installs | Command |
|---|---|---|---|---|---|
| Accordionaccordion | scrollxui | Components | Free | 4 deps | |
| Alert Dialogalert-dialog | scrollxui | Components | Free | 7 deps | |
| Animated Buttonanimated-button | scrollxui | Components | Free | 4 deps | |
| Animated Tabsanimated-tabs | scrollxui | Components | Free | 1 dep | |
| Animated Testimonialsanimated-testimonials | scrollxui | Components | Free | 2 deps | |
| Animated TextGenerateanimated-textgenerate | scrollxui | Components | Free | 3 deps | |
| Announcementannouncement | scrollxui | Components | Free | 5 deps | |
| Aspect Ratioaspect-ratio | scrollxui | Components | Free | 1 dep |
