Marquee
A scrolling text component that animates horizontally.
The Marquee component displays a continuous horizontal scrolling animation. It supports customizable speed, direction, and pause on hover functionality. Built from scratch using React and CSS animations. No dependencies on any UI library.
Code
TypeScript: Copy this code into components/ui/marquee.tsx:
tsx
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
interface MarqueeProps extends React.HTMLAttributes<HTMLDivElement> {
children: React.ReactNode
direction?: "left" | "right"
speed?: number
pauseOnHover?: boolean
className?: string
}
const Marquee = React.forwardRef<HTMLDivElement, MarqueeProps>(
({ children, direction = "left", speed = 50, pauseOnHover = false, className, ...props }, ref) => {
const [isPaused, setIsPaused] = React.useState(false)
const childrenArray = React.Children.toArray(children)
const duration = `${speed}s`
const animationName = direction === "left" ? "marquee" : "marquee-right"
return (
<div
ref={ref}
className={cn("overflow-hidden whitespace-nowrap border-t-2 border-b-2 border-foreground py-4", className)}
onMouseEnter={() => pauseOnHover && setIsPaused(true)}
onMouseLeave={() => pauseOnHover && setIsPaused(false)}
{...props}
>
<div
className="flex"
style={{
animationName: isPaused ? "none" : animationName,
animationDuration: isPaused ? "0s" : duration,
animationTimingFunction: "linear",
animationIterationCount: "infinite",
}}
>
{React.Children.map(childrenArray, (child, index) => (
<div key={`first-${index}`} className="flex-shrink-0 px-4">
{child}
</div>
))}
{React.Children.map(childrenArray, (child, index) => (
<div key={`second-${index}`} className="flex-shrink-0 px-4">
{child}
</div>
))}
</div>
</div>
)
}
)
Marquee.displayName = "Marquee"
interface MarqueeItemProps extends React.HTMLAttributes<HTMLDivElement> {}
const MarqueeItem = React.forwardRef<HTMLDivElement, MarqueeItemProps>(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex-shrink-0", className)}
{...props}
/>
)
)
MarqueeItem.displayName = "MarqueeItem"
export { Marquee, MarqueeItem }JavaScript: Copy this code into components/ui/marquee.jsx:
jsx
"use client"
import * as React from "react"
import { cn } from "@/lib/utils"
const Marquee = React.forwardRef(
({ children, direction = "left", speed = 50, pauseOnHover = false, className, ...props }, ref) => {
const [isPaused, setIsPaused] = React.useState(false)
const childrenArray = React.Children.toArray(children)
const duration = `${speed}s`
const animationName = direction === "left" ? "marquee" : "marquee-right"
return (
<div
ref={ref}
className={cn("overflow-hidden whitespace-nowrap border-t-2 border-b-2 border-foreground py-4", className)}
onMouseEnter={() => pauseOnHover && setIsPaused(true)}
onMouseLeave={() => pauseOnHover && setIsPaused(false)}
{...props}
>
<div
className="flex"
style={{
animationName: isPaused ? "none" : animationName,
animationDuration: isPaused ? "0s" : duration,
animationTimingFunction: "linear",
animationIterationCount: "infinite",
}}
>
{React.Children.map(childrenArray, (child, index) => (
<div key={`first-${index}`} className="flex-shrink-0 px-4">
{child}
</div>
))}
{React.Children.map(childrenArray, (child, index) => (
<div key={`second-${index}`} className="flex-shrink-0 px-4">
{child}
</div>
))}
</div>
</div>
)
}
)
Marquee.displayName = "Marquee"
const MarqueeItem = React.forwardRef(
({ className, ...props }, ref) => (
<div
ref={ref}
className={cn("flex-shrink-0", className)}
{...props}
/>
)
)
MarqueeItem.displayName = "MarqueeItem"
export { Marquee, MarqueeItem }Also add this CSS animation to your globals.css:
css
@keyframes marquee {
0% {
transform: translateX(0%);
}
100% {
transform: translateX(-50%);
}
}
@keyframes marquee-right {
0% {
transform: translateX(-50%);
}
100% {
transform: translateX(0%);
}
}Usage
TypeScript:
tsx
import { Marquee } from "@/components/ui/marquee"
function MyComponent() {
return (
<Marquee>
<span>Item 1</span>
<span>Item 2</span>
<span>Item 3</span>
<span>Item 4</span>
<span>Item 5</span>
</Marquee>
)
}JavaScript:
jsx
import { Marquee } from "@/components/ui/marquee"
function MyComponent() {
return (
<Marquee>
<span>Item 1</span>
<span>Item 2</span>
<span>Item 3</span>
<span>Item 4</span>
<span>Item 5</span>
</Marquee>
)
}Make sure you also have the lib/utils.ts file with the cn helper function.
Examples
Default
Item 1
Item 2
Item 3
Item 4
Item 5
Item 1
Item 2
Item 3
Item 4
Item 5
Right Direction
Item 1
Item 2
Item 3
Item 4
Item 5
Item 1
Item 2
Item 3
Item 4
Item 5
Faster Speed
Item 1
Item 2
Item 3
Item 4
Item 5
Item 1
Item 2
Item 3
Item 4
Item 5
Pause on Hover
Item 1
Item 2
Item 3
Item 4
Item 5
Item 1
Item 2
Item 3
Item 4
Item 5