Marquee.tsx

Animated scrolling marquee component in Plainform 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

On this page