No reduced motion preferences have been detected – animations will play

Marquee Component

A flexible, performant, and accessible marquee (scrolling text/content) component built with React and TypeScript.

Features


  • Minimal dependencies
  • Composable component structure
  • Headless (bring your own styles)
  • Responsive and adaptive
  • Accessible by default
  • TypeScript-first design
  • Smooth animations with GPU acceleration
  • Bidirectional scrolling support
  • Pause on hover capability
  • Optional gradient overlays

Dependencies


Peer Dependencies

  • React >= 16.8.0
  • @radix-ui/react-slot

Browser Requirements

  • Modern browsers with ResizeObserver support
  • requestAnimationFrame support

Installation

npm install rauler
yarn add rauler
pnpm add rauler

Basic Usage


import { Marquee } from "rauler"

function App() {
	return (
		<Marquee.Root>
			<Marquee.Content>
				{items.map((item) => (
					<Marquee.Item key={item.id}>{item.content}</Marquee.Item>
				))}
			</Marquee.Content>
		</Marquee.Root>
	)
}

Component API


Marquee.Root

PropTypeDefaultDescription
speednumber50Scrolling speed in pixels per second
direction'rtl' | 'ltr''ltr'Scroll direction
pauseOnHoverbooleanfalseWhether to pause on hover
isPausedbooleanfalseControlled pause state
classNamestringundefinedOptional CSS class
childrenReactNodeundefinedMarquee content

Marquee.Content

PropTypeDefaultDescription
classNamestringundefinedOptional CSS class
childrenReactNodeundefinedMarquee items

Marquee.Item

PropTypeDefaultDescription
asChildbooleanfalseWhether to merge props onto child element
classNamestringundefinedOptional CSS class
childrenReactNodeundefinedItem content

Marquee.Gradient

PropTypeDefaultDescription
side'start' | 'end'requiredWhich side to render the gradient
classNamestringundefinedOptional CSS class

Hooks


useMarquee

A custom React hook that provides smooth, infinite scrolling animation functionality with pause/resume capabilities and reduced motion support.

Hook Parameters

ParameterTypeDefaultDescription
speednumber50Scrolling speed in pixels per second
direction'ltr' | 'rtl''ltr'Scroll direction
isPausedbooleanfalseWhether the animation is paused
childrenReactNodeundefinedContent to be animated

Return Value

PropertyTypeDescription
containerRefReact.RefObjectRef for the container element
contentRefReact.RefObjectRef for the content element
loopCopiesnumber[]Array of indices for content copies
translatenumberCurrent translation value
contentWidthnumberWidth of the content
prefersReducedMotionbooleanUser's motion preference

Basic Hook Usage

function MarqueeComponent() {
	const {
		containerRef,
		contentRef,
		loopCopies,
		translate,
		contentWidth,
		prefersReducedMotion,
	} = useMarquee({
		speed: 50,
		direction: "ltr",
		isPaused: false,
	})

	return (
		<div ref={containerRef} style={{ overflow: "hidden" }}>
			<div
				ref={contentRef}
				style={{
					transform: `translateX(${translate}px)`,
					display: "flex",
					width: "max-content",
				}}
			>
				{/* Your content */}
				{loopCopies.map((i) => (
					<div key={i}>{/* Your content (duplicated) */}</div>
				))}
			</div>
		</div>
	)
}

With Pause on Hover

function MarqueeWithPause() {
	const [isPaused, setPaused] = useState(false)
	const { containerRef, contentRef, loopCopies, translate } = useMarquee({
		speed: 50,
		isPaused,
	})

	return (
		<div
			ref={containerRef}
			onMouseEnter={() => setPaused(true)}
			onMouseLeave={() => setPaused(false)}
		>
			<div
				ref={contentRef}
				style={{
					transform: `translateX(${translate}px)`,
					display: "flex",
					width: "max-content",
				}}
			>
				{/* Your content */}
				{loopCopies.map((i) => (
					<div key={i}>{/* Your content (duplicated) */}</div>
				))}
			</div>
		</div>
	)
}

Advanced Examples


Using Custom Elements with asChild

<Marquee.Root direction="rtl" pauseOnHover>
	<Marquee.Content>
		{items.map((item) => (
			<Marquee.Item asChild key={item.id}>
				<button onClick={() => console.log(item.id)}>{item.content}</button>
			</Marquee.Item>
		))}
	</Marquee.Content>
</Marquee.Root>

With Gradient Overlays

<Marquee.Root speed={100}>
	<Marquee.Gradient
		side="start"
		className="w-24 bg-gradient-to-r from-background to-transparent"
	/>
	<Marquee.Content>
		{items.map((item) => (
			<Marquee.Item key={item.id}>{item.content}</Marquee.Item>
		))}
	</Marquee.Content>
	<Marquee.Gradient
		side="end"
		className="w-24 bg-gradient-to-l from-background to-transparent"
	/>
</Marquee.Root>

Best Practices


  1. Content Width
    Ensure your content items have a fixed or determinable width for proper animation calculations.

  2. Container Overflow
    The root container should have overflow: hidden (applied automatically).

  3. Direction Changes
    The component handles direction changes smoothly, but avoid frequent changes for better performance.

  4. Gradient Positioning
    When using gradients, ensure they're positioned relative to the root container.

  5. Using asChild
    When using asChild, ensure the child element can accept and properly handle the props being passed down.

Contributing

Contributions are welcome! Please read our contributing guidelines and code of conduct before submitting pull requests.

License

MIT © Rauler