Marquee - Infinite Scroll Component
An infinite scrolling marquee for text, images, logos, or any HTML. Supports left, right, up, and down directions with pause-on-hover, edge fade, and 3D layouts.
An infinite scrolling marquee for text, images, logos, or any HTML. Supports left, right, up, and down directions with pause-on-hover, edge fade, and 3D layouts.
npx shadcn@latest add "https://ui-struct.vercel.app/r/marquee"
Add these animations to your tailwind.config.ts (if not already present):
// theme.extend.animationmarquee: 'marquee var(--duration) linear infinite','marquee-vertical': 'marquee-vertical var(--duration) linear infinite',// theme.extend.keyframesmarquee: {from: { transform: 'translateX(0)' },to: { transform: 'translateX(calc(-100% - var(--gap)))' },},'marquee-vertical': {from: { transform: 'translateY(0)' },to: { transform: 'translateY(calc(-100% - var(--gap)))' },},
import Marquee from "@/components/ui/marquee";// or: import { Marquee } from "@/components/ui/marquee";<Marquee pauseOnHover><div>Item 1</div><div>Item 2</div><div>Item 3</div></Marquee>
Two horizontal rows scrolling in opposite directions. Hover to pause.
“I've never seen anything like this before. It's amazing. I love it.”
Product Designer·@jack
“I don't know what to say. I'm speechless. This is amazing.”
Frontend Engineer·@jill
“I'm at a loss for words. This is amazing. I love it.”
Founder·@john
“This is hands down the best UI library I've used. Incredible.”
Design Lead·@jane
“Clean, fast, and beautiful. Exactly what I needed for my project.”
Full-stack Dev·@jenny
“I'm at a loss for words. This is amazing. I love it.”
CTO·@james
Scroll left, right, up, or down with the direction prop.
Left (default)
Right
Up
Down
| Direction | Description |
|---|---|
left | Horizontal, moving left (default feel) |
right | Horizontal, moving right |
up | Vertical, moving up |
down | Vertical, moving down |
You can also use vertical and reverse instead of direction.
Side-by-side vertical marquees — great for testimonials.
“I've never seen anything like this before. It's amazing. I love it.”
Product Designer·@jack
“I don't know what to say. I'm speechless. This is amazing.”
Frontend Engineer·@jill
“I'm at a loss for words. This is amazing. I love it.”
Founder·@john
“This is hands down the best UI library I've used. Incredible.”
Design Lead·@jane
“Clean, fast, and beautiful. Exactly what I needed for my project.”
Full-stack Dev·@jenny
“I'm at a loss for words. This is amazing. I love it.”
CTO·@james
Stack vertical marquees with a 3D transform for a depth effect.
“I've never seen anything like this before. It's amazing. I love it.”
Product Designer·@jack
“I don't know what to say. I'm speechless. This is amazing.”
Frontend Engineer·@jill
“I'm at a loss for words. This is amazing. I love it.”
Founder·@john
“This is hands down the best UI library I've used. Incredible.”
Design Lead·@jane
“Clean, fast, and beautiful. Exactly what I needed for my project.”
Full-stack Dev·@jenny
“I'm at a loss for words. This is amazing. I love it.”
CTO·@james
“I've never seen anything like this before. It's amazing. I love it.”
Product Designer·@jack
“I don't know what to say. I'm speechless. This is amazing.”
Frontend Engineer·@jill
“I'm at a loss for words. This is amazing. I love it.”
Founder·@john
“This is hands down the best UI library I've used. Incredible.”
Design Lead·@jane
“Clean, fast, and beautiful. Exactly what I needed for my project.”
Full-stack Dev·@jenny
“I'm at a loss for words. This is amazing. I love it.”
CTO·@james
Works with any HTML — logos, badges, or plain text.
Skill tags and cards with brand icons — great for tech stacks and tool belts.
| Prop | Type | Default | Description |
|---|---|---|---|
children | ReactNode | - | Items to scroll (text, images, or any HTML) |
direction | 'left' | 'right' | 'up' | 'down' | - | Scroll direction (overrides vertical / reverse) |
vertical | boolean | false | Vertical orientation |
reverse | boolean | false | Reverse animation direction |
pauseOnHover | boolean | false | Pause while hovering |
repeat | number | 4 | Copies of children for a seamless loop |
duration | number | 40 | Animation duration in seconds |
gap | string | '1rem' | Gap between items |
fade | boolean | true | Soft fade on the edges |
className | string | - | Additional CSS classes |
aria-hidden'use client';import React from 'react';import { cn } from '@/lib/utils';export type MarqueeDirection = 'left' | 'right' | 'up' | 'down';export interface MarqueeProps {children: React.ReactNode;className?: string;/** Scroll direction. Overrides `vertical` / `reverse` when set. */direction?: MarqueeDirection;/** Vertical scroll (ignored when `direction` is set) */vertical?: boolean;/** Reverse animation direction (ignored when `direction` is set) */reverse?: boolean;/** Pause animation while hovered */pauseOnHover?: boolean;/** How many times to repeat children for a seamless loop */repeat?: number;/** Animation duration in seconds */duration?: number;/** Gap between items (CSS length) */gap?: string;/** Fade edges with a mask */fade?: boolean;}function resolveOrientation(direction: MarqueeDirection | undefined,vertical: boolean,reverse: boolean) {if (direction) {return {isVertical: direction === 'up' || direction === 'down',isReverse: direction === 'right' || direction === 'down',};}return { isVertical: vertical, isReverse: reverse };}function Marquee({children,className,direction,vertical = false,reverse = false,pauseOnHover = true,repeat = 4,duration = 40,gap = '1rem',fade = true,}: MarqueeProps) {const { isVertical, isReverse } = resolveOrientation(direction,vertical,reverse);const maskImage = fade? isVertical? 'linear-gradient(to bottom, transparent, black 12%, black 88%, transparent)': 'linear-gradient(to right, transparent, black 12%, black 88%, transparent)': undefined;return (<divclassName={cn('group flex overflow-hidden p-2',isVertical ? 'flex-col' : 'flex-row',className)}style={{'--duration': `${duration}s`,'--gap': gap,gap: 'var(--gap)',maskImage,WebkitMaskImage: maskImage,} as React.CSSProperties}>{Array.from({ length: repeat }).map((_, i) => (<divkey={i}className={cn('flex shrink-0 justify-around',isVertical? 'animate-marquee-vertical flex-col': 'animate-marquee flex-row',isReverse && '[animation-direction:reverse]',pauseOnHover && 'group-hover:[animation-play-state:paused]')}style={{ gap: 'var(--gap)' }}aria-hidden={i > 0 || undefined}>{children}</div>))}</div>);}export { Marquee };// @docs-preview-startexport default function MarqueeDocsPreview() {const items = ['React', 'Next.js', 'Tailwind', 'TypeScript', 'Framer'];return (<div className="relative flex h-[160px] w-full items-center justify-center overflow-hidden"><Marquee pauseOnHover duration={20}>{items.map((item) => (<divkey={item}className="rounded-full border border-zinc-200 bg-zinc-50 px-4 py-1.5 text-sm font-medium text-zinc-700 dark:border-zinc-700 dark:bg-zinc-900 dark:text-zinc-200">{item}</div>))}</Marquee></div>);}// @docs-preview-end