NEW
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
shadcn registry is live·
Learn more
Skip to content
Docs

Marquee

A marquee component

requires configrequires interactionhover
Loading...

Installation

CLI

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

Manual

Add to your CSS

@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))); }
}
@theme {
  --animate-marquee-horizontal: marquee-x var(--duration) infinite linear;
  --animate-marquee-vertical: marquee-y var(--duration) linear infinite;
}

Run the following command

It will create a new file called marquee.tsx inside the components/animata/container directory.

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

Paste the code

Open the newly created file and paste the following code:

import { cn } from "@/lib/utils";
 
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,
      )}
    >
      <style>{`
        @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;
        }
      `}</style>
      {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