This component no longer exists. It was in ruixen-ui’s manifest when we first indexed it and is not there now — the registry served its inventory on 2026-08-12 and this item was not on it. The install command below will fail. It is kept here because a tutorial somewhere still tells people to run it.
Avatar Spring Stack
ruixen-ui/avatar-spring-stackRemovedComponent
An overlapping avatar stack with spring-animated tooltips that reveal labels on hover, plus a +N overflow counter.
Live preview
live · rendered by the registry
Rendered by ruixen-ui on their own page — this is the component running, not a screenshot of it.Install it
npx shadcn@latest add https://ruixen.com/r/avatar-spring-stack.jsonSource
1"use client";23import { Avatar, AvatarFallback, AvatarImage } from "@/components/ui/avatar";4import { cn } from "@/lib/utils";5import { AnimatePresence, motion } from "motion/react";6import { useState } from "react";78/* ═══════════════════════════════════════════════════════════9 Magnetic Repulsion — each avatar is a charged body. Hover10 one and neighbors spring away in proportion to 1/distance,11 creating a lens-like spread that follows your cursor.12 The wave isn't animated — it emerges from the spring physics.13 ═══════════════════════════════════════════════════════════ */1415interface AvatarSpringStackItem {16 src: string;17 alt?: string;18 label?: string;19}2021interface AvatarSpringStackProps {22 avatars: AvatarSpringStackItem[];23 maxVisible?: number;24 size?: number;25 className?: string;26}2728export default function AvatarSpringStack({29 avatars,30 maxVisible = 5,31 size = 44,32 className,33}: AvatarSpringStackProps) {34 const [activeIdx, setActiveIdx] = useState<number | null>(null);3536 const visible = avatars.slice(0, maxVisible);37 const extra = avatars.length - maxVisible;38 const total = visible.length + (extra > 0 ? 1 : 0);3940 const overlap = Math.round(size * 0.32);41 const step = size - overlap;42 const spread = Math.round(size * 0.45);4344 function getOffset(idx: number) {45 if (activeIdx === null) return 0;46 const d = idx - activeIdx;47 if (d === 0) return 0;48 return (d > 0 ? 1 : -1) * (spread / Math.abs(d));49 }5051 const hasActive = activeIdx !== null;5253 return (54 <div55 className={cn("relative", className)}56 style={{ width: (total - 1) * step + size, height: size }}57 onMouseLeave={() => setActiveIdx(null)}58 >59 {visible.map((avatar, idx) => {60 const active = activeIdx === idx;6162 return (63 <motion.div64 key={idx}65 className="absolute top-0 cursor-pointer"66 animate={{67 x: idx * step + getOffset(idx),68 y: active ? -6 : 0,69 scale: active ? 1.08 : 1,70 }}71 transition={{72 x: { type: "spring", stiffness: 500, damping: 30 },73 y: { type: "spring", stiffness: 420, damping: 22 },74// … truncated
What it pulls in
npm packages
Other registry items
