Marquee
marquee ui Continuously auto-scrolling content. Scrolls horizontally or vertically in any direction, with configurable speed, gap, repeat count for a seamless loop, pause-on-hover, and a hard pause prop. Respects prefers-reduced-motion.
Also available for Vue ->Installation
$ pnpm dlx shadcn@latest add https://uipkge.dev/r/react/marquee.json $ npx shadcn@latest add https://uipkge.dev/r/react/marquee.json $ yarn dlx shadcn@latest add https://uipkge.dev/r/react/marquee.json $ bunx shadcn@latest add https://uipkge.dev/r/react/marquee.json Named registry:
npx shadcn@latest add @uipkge-react/marquee Installs to: components/ui/marquee/ Examples
Props
| Name | Type / Values | Default | Required |
|---|---|---|---|
orientation Scroll axis. | 'horizontal' | 'vertical' | — | optional |
direction Travel direction. | 'left' | 'right' | 'up' | 'down' | — | optional |
speed Animation duration in seconds. Lower = faster. | number | — | optional |
pauseOnHover Pause the animation on hover. | boolean | — | optional |
gap Gap between repeated content groups (px). | number | — | optional |
repeat Number of times the slot content is duplicated for a seamless loop. | number | — | optional |
paused Hard pause the animation. | boolean | — | optional |
Files installed (2)
-
components/ui/marquee/Marquee.tsx 3.8 kB
'use client' import * as React from 'react' import { cn } from '@/lib/utils' export interface MarqueeProps extends React.HTMLAttributes<HTMLDivElement> { /** Scroll axis. */ orientation?: 'horizontal' | 'vertical' /** Travel direction. */ direction?: 'left' | 'right' | 'up' | 'down' /** Animation duration in seconds. Lower = faster. */ speed?: number /** Pause the animation on hover. */ pauseOnHover?: boolean /** Gap between repeated content groups (px). */ gap?: number /** Number of times the slot content is duplicated for a seamless loop. */ repeat?: number /** Hard pause the animation. */ paused?: boolean } /* ------------------------------------------------------------------ */ /* Marquee keyframes */ /* Ported from Marquee.vue's non-scoped <style> block. Injected as a */ /* global <style> so the inline animationName can resolve the */ /* @keyframes (scoped hashes would break the name lookup). */ /* ------------------------------------------------------------------ */ const marqueeCss = ` @keyframes uipkge-marquee-x { from { transform: translateX(0); } to { transform: translateX(-100%); } } @keyframes uipkge-marquee-y { from { transform: translateY(0); } to { transform: translateY(-100%); } } @media (prefers-reduced-motion: reduce) { [data-slot='marquee-track'] { animation: none !important; } } ` function MarqueeStyle() { return <style dangerouslySetInnerHTML={{ __html: marqueeCss }} /> } const Marquee = React.forwardRef<HTMLDivElement, MarqueeProps>( ( { className, orientation = 'horizontal', direction = 'left', speed = 20, pauseOnHover = false, gap = 16, repeat = 2, paused = false, style, children, ...props }, ref, ) => { const isVertical = orientation === 'vertical' const reverse = direction === 'right' || direction === 'down' const containerClass = React.useMemo( () => cn( 'group flex overflow-hidden', isVertical ? 'flex-col' : 'flex-row', pauseOnHover ? 'hover:[&>[data-slot=marquee-track]]:[animation-play-state:paused]' : '', className, ), [isVertical, pauseOnHover, className], ) const trackClass = React.useMemo( () => cn('flex shrink-0', isVertical ? 'flex-col' : 'flex-row', paused ? '![animation-play-state:paused]' : ''), [isVertical, paused], ) const trackStyle = React.useMemo<React.CSSProperties>( () => ({ gap: 'var(--marquee-gap)', animationName: isVertical ? 'uipkge-marquee-y' : 'uipkge-marquee-x', animationDuration: `${speed}s`, animationTimingFunction: 'linear', animationIterationCount: 'infinite', animationDirection: reverse ? 'reverse' : 'normal', }), [isVertical, speed, reverse], ) const containerStyle = React.useMemo<React.CSSProperties>( () => ({ '--marquee-gap': `${gap}px`, ...style }) as React.CSSProperties, [gap, style], ) return ( <> <MarqueeStyle /> <div ref={ref} data-uipkge="" data-slot="marquee" data-orientation={orientation} data-direction={direction} className={containerClass} style={containerStyle} {...props} > {Array.from({ length: repeat }, (_, i) => ( <div key={i} data-slot="marquee-track" className={trackClass} style={trackStyle} aria-hidden={i > 1 ? true : undefined} > {children} </div> ))} </div> </> ) }, ) Marquee.displayName = 'Marquee' export { Marquee } -
components/ui/marquee/index.ts 0.1 kB
export { Marquee, type MarqueeProps } from './Marquee'
Raw manifest: https://uipkge.dev/r/react/marquee.json