docsStacked Logos
Stacked Logos
Multiple logo groups that animate in/out while stacked on top of each other. Features a Vercel-style grid with mouse-following glow effect on borders.
Loading Preview...
Install using CLI
npx shadcn@latest add "https://www.vengenceui.com/r/stacked-logos.json"Install Manually
1
Add util file
lib/utils.ts
import { ClassValue, clsx } from "clsx";import { twMerge } from "tailwind-merge";export function cn(...inputs: ClassValue[]) {return twMerge(clsx(inputs));}
2
Add styles to global CSS
Add the following styles to your app/globals.css file
/* StackedLogos - CSS Variables & Animations */.stacked-logos {--duration: 30;--items: 4;--lists: 4;--stagger: 0;--logo-width: 200px;--mouse-x: 0px;--mouse-y: 0px;}.stacked-logos:hover .stacked-logos__glow {opacity: 1;}.stacked-logos:hover .stacked-logos__border-glow {opacity: 1;}.stacked-logos__cell {--base-delay: calc(sin((var(--index) / var(--lists)) * 45deg) * var(--stagger));}.stacked-logos__logo {animation-name: stacked-logos-appear;animation-duration: calc(var(--duration) * 1s);animation-fill-mode: both;animation-iteration-count: infinite;animation-delay: calc((var(--duration) / var(--items)) * (var(--items) - var(--i)) * -1s +(var(--base-delay, 0) * 1s));}@keyframes stacked-logos-appear {0%, 100% { opacity: 0; filter: blur(4px); }5%, 20% { opacity: 1; filter: blur(0); }25%, 100% { opacity: 0; filter: blur(4px); }}@media (prefers-reduced-motion: reduce) {.stacked-logos__logo {animation: none;opacity: 1;filter: none;}.stacked-logos__item:not(:first-child) {display: none;}}
3
Copy the source code
Copy the code below and paste it into components/ui/stacked-logos.tsx
"use client";import * as React from "react";import { cn } from "@/lib/utils";export interface StackedLogosProps {logoGroups: React.ReactNode[][];duration?: number;stagger?: number;logoWidth?: string;className?: string;}export const StackedLogos = ({logoGroups,duration = 30,stagger = 0,logoWidth = "200px",className,}: StackedLogosProps) => {const itemCount = logoGroups[0]?.length || 0;const columns = logoGroups.length;const containerRef = React.useRef<HTMLDivElement>(null);const gridRef = React.useRef<HTMLDivElement>(null);const handleMouseMove = React.useCallback((e: React.MouseEvent<HTMLDivElement>) => {if (!containerRef.current || !gridRef.current) return;const rect = gridRef.current.getBoundingClientRect();const x = e.clientX - rect.left;const y = e.clientY - rect.top;containerRef.current.style.setProperty('--mouse-x', `${x}px`);containerRef.current.style.setProperty('--mouse-y', `${y}px`);}, []);return (<divref={containerRef}className={cn("stacked-logos relative w-full", className)}style={{"--duration": duration,"--items": itemCount,"--lists": columns,"--stagger": stagger,"--logo-width": logoWidth,} as React.CSSProperties}onMouseMove={handleMouseMove}><div ref={gridRef} className="grid relative" style={{ gridTemplateColumns: `repeat(${columns}, ${logoWidth})` }}>{/* Background glow */}<div className="stacked-logos__glow pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-300 z-10"style={{ background: 'radial-gradient(500px circle at var(--mouse-x, 0) var(--mouse-y, 0), rgba(251,191,36,0.1), transparent 70%)' }} />{/* Border glow */}<div className="stacked-logos__border-glow pointer-events-none absolute inset-0 opacity-0 transition-opacity duration-300 z-20"style={{background: 'radial-gradient(600px circle at var(--mouse-x, 0) var(--mouse-y, 0), rgba(251,191,36,1), transparent 40%)',maskImage: `repeating-linear-gradient(to right, transparent, transparent calc(${logoWidth} - 1px), black calc(${logoWidth} - 1px), black ${logoWidth}), linear-gradient(to bottom, black 0, black 1px, transparent 1px, transparent calc(100% - 1px), black calc(100% - 1px), black 100%)`,WebkitMaskImage: `repeating-linear-gradient(to right, transparent, transparent calc(${logoWidth} - 1px), black calc(${logoWidth} - 1px), black ${logoWidth}), linear-gradient(to bottom, black 0, black 1px, transparent 1px, transparent calc(100% - 1px), black calc(100% - 1px), black 100%)`,maskComposite: 'add',}} />{/* Left edge glow */}<div className="stacked-logos__border-glow pointer-events-none absolute top-0 bottom-0 left-0 w-px opacity-0 transition-opacity duration-300 z-20"style={{ background: 'radial-gradient(600px circle at var(--mouse-x, 0) var(--mouse-y, 0), rgba(251,191,36,1), transparent 40%)' }} />{logoGroups.map((logos, groupIndex) => (<div key={groupIndex} className="stacked-logos__cell relative grid"style={{ "--index": groupIndex, gridTemplate: "1fr / 1fr" } as React.CSSProperties}>{/* Border lines */}<div className="absolute top-0 bottom-0 right-0 w-px bg-zinc-200 dark:bg-zinc-800" /><div className="absolute left-0 right-0 bottom-0 h-px bg-zinc-200 dark:bg-zinc-800" /><div className="absolute left-0 right-0 top-0 h-px bg-zinc-200 dark:bg-zinc-800" />{groupIndex === 0 && <div className="absolute top-0 bottom-0 left-0 w-px bg-zinc-200 dark:bg-zinc-800" />}{logos.map((logo, logoIndex) => (<div key={logoIndex} className="stacked-logos__item col-start-1 row-start-1 grid place-items-center py-16 px-8"style={{ "--i": logoIndex } as React.CSSProperties}><div className="stacked-logos__logo w-full h-8 flex items-center justify-center [&>svg]:h-full [&>svg]:w-auto [&>svg]:fill-zinc-700 dark:[&>svg]:fill-zinc-300">{logo}</div></div>))}</div>))}</div></div>);};StackedLogos.displayName = "StackedLogos";export default StackedLogos;
Usage
1import { StackedLogos } from "@/components/ui/stacked-logos"23const logos1 = [<Logo1 />, <Logo2 />, <Logo3 />, <Logo4 />]4const logos2 = [<Logo5 />, <Logo6 />, <Logo7 />, <Logo8 />]56export function PartnersSection() {7return (8 <StackedLogos9 logoGroups={[logos1, logos2, logos3, logos4]}10 duration={20}11 stagger={2}12 logoWidth="200px"13 />14)15}
Fast Animation
Loading Preview...
Slow Animation
Loading Preview...
Two Groups Only
Loading Preview...
Custom Width
Loading Preview...
Props
| Prop Name | Type | Default | Description |
|---|---|---|---|
| logoGroups | React.ReactNode[][] | - | Array of logo groups. Each group is an array of logo elements. |
| duration | number | 30 | Full animation cycle duration in seconds. |
| stagger | number | 0 | Stagger factor for timing offset between groups. |
| logoWidth | string | "200px" | Width of each logo container. |
| className | string | - | Additional classes. |