Display15
Form1
Playground3
Infinite Marquee
A component for displaying a continuous scrolling content.
Infinite Marquee
Live controls for speed, hover pause/scale, and logo size; reset remounts the track so the animation restarts
infinite-marquee-example.tsx
1"use client";23import React from "react";4import Image from "next/image";5import { cva, type VariantProps } from "class-variance-authority";67import { cn } from "@/lib/utils";89const marqueeRootVariants = cva("relative flex w-full overflow-x-hidden", {10 variants: {11 pauseOnHover: {12 true: "infinite-marquee-root",13 false: null,14 },15 },16 defaultVariants: {17 pauseOnHover: true,18 },19});2021const marqueeTrackVariants = cva(22 "flex min-w-full shrink-0 items-center justify-around gap-4 py-5",23 {24 variants: {25 speed: {26 slow: "animate-marquee-slow",27 normal: "animate-marquee",28 fast: "animate-marquee-fast",29 },30 pauseOnHover: {31 true: "infinite-marquee-track",32 false: null,33 },34 },35 defaultVariants: {36 speed: "normal",37 pauseOnHover: true,38 },39 }40);4142const marqueeImageVariants = cva("h-auto w-auto", {43 variants: {44 scaleOnHover: {45 true: "transition-all duration-300 hover:scale-110",46 false: null,47 },48 },49 defaultVariants: {50 scaleOnHover: true,51 },52});5354interface InfiniteMarqueeProps55 extends VariantProps<typeof marqueeRootVariants>,56 VariantProps<typeof marqueeTrackVariants>,57 VariantProps<typeof marqueeImageVariants> {58 logos?: Logo[];59 imageWidth?: number;60 imageHeight?: number;61 className?: string;62}6364export interface Logo {65 src: string;66 alt: string;67}6869const defaultLogos: Logo[] = [70 {71 src: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/react/react-original.svg",72 alt: "React",73 },74 {75 src: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/javascript/javascript-original.svg",76 alt: "JavaScript",77 },78 {79 src: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/typescript/typescript-original.svg",80 alt: "TypeScript",81 },82 {83 src: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/nodejs/nodejs-original.svg",84 alt: "Node.js",85 },86 {87 src: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/html5/html5-original.svg",88 alt: "HTML5",89 },90 {91 src: "https://cdn.jsdelivr.net/gh/devicons/devicon@latest/icons/css3/css3-original.svg",92 alt: "CSS3",93 },94];9596export default function InfiniteMarquee({97 logos = defaultLogos,98 pauseOnHover = true,99 scaleOnHover = true,100 speed = "normal",101 imageWidth = 100,102 imageHeight = 100,103 className,104}: InfiniteMarqueeProps) {105 const isExplicitlyFalse = (v: unknown) =>106 v === false ||107 v === 0 ||108 (typeof v === "string" && v.toLowerCase() === "false");109110 const pause = !isExplicitlyFalse(pauseOnHover);111 const scale = !isExplicitlyFalse(scaleOnHover);112 const speedSafe =113 speed === "slow" || speed === "normal" || speed === "fast"114 ? speed115 : "normal";116 const w = Math.round(117 Number.isFinite(Number(imageWidth)) ? Number(imageWidth) : 100,118 );119 const h = Math.round(120 Number.isFinite(Number(imageHeight)) ? Number(imageHeight) : 100,121 );122123 const marqueeContent = (124 <>125 {logos.map((logo, index) => (126 <div127 key={index}128 className="flex items-center justify-center"129 style={{ width: `${w}px` }}130 >131 <Image132 src={logo.src}133 alt={logo.alt}134 width={w}135 height={h}136 className={marqueeImageVariants({ scaleOnHover: scale })}137 />138 </div>139 ))}140 </>141 );142143 return (144 <div className={cn(marqueeRootVariants({ pauseOnHover: pause }), className)}>145 <div className={marqueeTrackVariants({ speed: speedSafe, pauseOnHover: pause })}>146 {marqueeContent}147 {marqueeContent}148 </div>149 <div className={marqueeTrackVariants({ speed: speedSafe, pauseOnHover: pause })}>150 {marqueeContent}151 {marqueeContent}152 </div>153 </div>154 );155}
Props
| Name | Type | Default | Description |
|---|---|---|---|
| logos | Logo[] | defaultLogos | Array of logo objects to display in the marquee |
| pauseOnHover | boolean | true | Whether to pause the animation on hover |
| speed | 'slow' | 'normal' | 'fast' | 'normal' | Animation speed of the marquee |
| scaleOnHover | boolean | true | Whether to scale logos on hover |
| imageWidth | number | 100 | Width of the logo images in pixels |
| imageHeight | number | 100 | Height of the logo images in pixels |