We use tracking cookies to understand how you use the product and help us improve it. For more information on how we store cookies, read our  privacy policy.

Marquee.tsx

Animated scrolling marquee component for displaying repeating content

The Marquee component creates smooth, infinite scrolling animations for displaying logos, testimonials, or any repeating content.

Features

  • Horizontal or vertical scrolling
  • Configurable animation speed
  • Pause on hover option
  • Reverse direction support
  • Customizable repeat count
  • Smooth CSS animations

Basic Usage

app/page.tsx
import { Marquee } from '@/components/ui/Marquee';

export default function Page() {
  return (
    <Marquee>
      <div className="mx-4">Item 1</div>
      <div className="mx-4">Item 2</div>
      <div className="mx-4">Item 3</div>
      <div className="mx-4">Item 4</div>
    </Marquee>
  );
}

Logo Showcase

app/page.tsx
import { Marquee } from '@/components/ui/Marquee';
import Image from 'next/image';

export default function LogoMarquee() {
  const logos = [
    { name: 'Company 1', src: '/logos/company1.png' },
    { name: 'Company 2', src: '/logos/company2.png' },
    { name: 'Company 3', src: '/logos/company3.png' },
  ];

  return (
    <Marquee pauseOnHover className="[--duration:20s]">
      {logos.map((logo) => (
        <div key={logo.name} className="mx-8">
          <Image
            src={logo.src}
            alt={logo.name}
            width={120}
            height={40}
            className="opacity-50 hover:opacity-100 transition-opacity"
          />
        </div>
      ))}
    </Marquee>
  );
}

Testimonials

app/page.tsx
import { Marquee } from '@/components/ui/Marquee';
import { Card, CardContent } from '@/components/ui/Card';

export default function TestimonialMarquee() {
  const testimonials = [
    { name: 'John Doe', text: 'Great product!' },
    { name: 'Jane Smith', text: 'Highly recommend!' },
    { name: 'Bob Johnson', text: 'Amazing experience!' },
  ];

  return (
    <Marquee pauseOnHover className="[--duration:30s]">
      {testimonials.map((testimonial) => (
        <Card key={testimonial.name} className="mx-4 w-[300px]">
          <CardContent className="p-4">
            <p className="text-sm">{testimonial.text}</p>
            <p className="text-xs text-neutral-foreground mt-2">
              - {testimonial.name}
            </p>
          </CardContent>
        </Card>
      ))}
    </Marquee>
  );
}

Vertical Marquee

app/page.tsx
<Marquee vertical className="h-[400px]">
  <div className="my-4">Item 1</div>
  <div className="my-4">Item 2</div>
  <div className="my-4">Item 3</div>
</Marquee>

Reverse Direction

app/page.tsx
<Marquee reverse>
  <div className="mx-4">Item 1</div>
  <div className="mx-4">Item 2</div>
  <div className="mx-4">Item 3</div>
</Marquee>

Custom Speed

Control animation speed with CSS variable:

app/page.tsx
<Marquee className="[--duration:10s]">
  <div className="mx-4">Fast scrolling</div>
</Marquee>

<Marquee className="[--duration:60s]">
  <div className="mx-4">Slow scrolling</div>
</Marquee>

Custom Gap

Adjust spacing between items:

app/page.tsx
<Marquee className="[--gap:2rem]">
  <div>Item 1</div>
  <div>Item 2</div>
  <div>Item 3</div>
</Marquee>

Multiple Rows

Create multiple marquee rows with different directions:

app/page.tsx
<div className="space-y-4">
  <Marquee className="[--duration:20s]">
    <div className="mx-4">Row 1 - Item 1</div>
    <div className="mx-4">Row 1 - Item 2</div>
  </Marquee>
  
  <Marquee reverse className="[--duration:25s]">
    <div className="mx-4">Row 2 - Item 1</div>
    <div className="mx-4">Row 2 - Item 2</div>
  </Marquee>
</div>

Implementation

The Marquee uses CSS animations defined in globals.css:

components/styles/globals.css
@theme inline {
  --animate-marquee: marquee var(--duration) infinite linear;
  --animate-marquee-vertical: marquee-vertical var(--duration) linear infinite;

  @keyframes marquee {
    from {
      transform: translateX(0);
    }
    to {
      transform: translateX(calc(-100% - var(--gap)));
    }
  }

  @keyframes marquee-vertical {
    from {
      transform: translateY(0);
    }
    to {
      transform: translateY(calc(-100% - var(--gap)));
    }
  }
}

Props

interface IMarqueeProps {
  className?: string;
  reverse?: boolean;        // Reverse animation direction
  pauseOnHover?: boolean;   // Pause on hover
  vertical?: boolean;       // Vertical scrolling
  repeat?: number;          // Number of repetitions (default: 4)
  children: React.ReactNode;
}

CSS Variables

  • --duration: Animation duration (default: 40s)
  • --gap: Gap between items (default: 1rem)

Best Practices

  • Use pauseOnHover for interactive content
  • Adjust repeat based on content width
  • Set appropriate --duration for smooth scrolling
  • Use consistent spacing between items
  • Ensure content is readable at scroll speed

How is this guide ?

Last updated on