Skip to content
Docs
Marquee

Marquee

A marquee component

requires configrequires interactionhover
Loading...

Installation

CLI

pnpm dlx shadcn@latest add https://animata.design/r/container/marquee.json

Manual

Run the following command

It will create marquee.tsx and the co-located marquee.css inside components/animata/container.

mkdir -p components/animata/container && touch components/animata/container/marquee.tsx components/animata/container/marquee.css

Paste the code

@layer components {
  @keyframes marquee-x {
    from {
      transform: translateX(0);
    }
 
    to {
      transform: translateX(calc(-100% - var(--gap)));
    }
  }
 
  @keyframes marquee-y {
    from {
      transform: translateY(0);
    }
 
    to {
      transform: translateY(calc(-100% - var(--gap)));
    }
  }
 
  .marquee-horizontal {
    animation: marquee-x var(--duration) infinite linear;
  }
 
  .marquee-vertical {
    animation: marquee-y var(--duration) infinite linear;
  }
 
  .group\/marquee:hover .marquee-pause-on-hover {
    animation-play-state: paused;
  }
}
import { cn } from "@/lib/utils";
 
import "./marquee.css";
 
interface MarqueeProps extends React.HTMLAttributes<HTMLDivElement> {
  /**
   * Should the marquee scroll horizontally or vertically.
   * If set to `true`, the marquee will scroll vertically.
   *
   * @default false
   */
  vertical?: boolean;
 
  /**
   * The number of times to repeat the children. Set this value so that the repeated children overflow the container.
   * @default 5
   */
  repeat?: number;
 
  /**
   * Reverse the marquee direction.
   */
  reverse?: boolean;
 
  /**
   * Pause the marquee animation on hover.
   */
  pauseOnHover?: boolean;
 
  /**
   * Apply a gradient mask to the marquee.
   * @default true
   */
  applyMask?: boolean;
}
 
export default function Marquee({
  children,
  vertical = false,
  repeat = 5,
  pauseOnHover = false,
  reverse = false,
  className,
  applyMask = true,
  ...props
}: MarqueeProps) {
  return (
    <div
      {...props}
      className={cn(
        "group/marquee relative flex h-full w-full p-2 [--duration:10s] [--gap:12px] [gap:var(--gap)]",
        {
          "flex-col": vertical,
          "flex-row": !vertical,
        },
        className,
      )}
    >
      {Array.from({ length: repeat }).map((_, index) => (
        <div
          key={`item-${index}`}
          className={cn("flex shrink-0 [gap:var(--gap)]", {
            "marquee-pause-on-hover": pauseOnHover,
            "marquee-horizontal flex-row": !vertical,
            "marquee-vertical flex-col": vertical,
          })}
          style={reverse ? { animationDirection: "reverse" } : undefined}
        >
          {children}
        </div>
      ))}
      {applyMask && (
        <div
          className={cn(
            "pointer-events-none absolute inset-0 z-10 h-full w-full from-white/50 from-5% via-transparent via-50% to-white/50 to-95% dark:from-gray-800/50 dark:via-transparent dark:to-gray-800/50",
            {
              "bg-linear-to-b": vertical,
              "bg-linear-to-r": !vertical,
            },
          )}
        />
      )}
    </div>
  );
}

Credits

Built by hari