Marquee
A seamless scrolling row or column, in either direction, that pauses on hover.
Installation
npx shadcn@latest add @loomui/marqueeUsage
import { Marquee } from "@/components/ui/marquee"<Marquee pauseOnHover duration={28}>
{quotes.map((quote) => (
<figure key={quote} className="w-64 shrink-0 rounded-xl border p-4">
<blockquote className="text-sm">{quote}</blockquote>
</figure>
))}
</Marquee>The children are rendered twice. The track travels exactly one copy plus one
gap, then snaps back, and because the second copy is sitting where the first one
started the seam is invisible. Copies after the first are aria-hidden.
fade softens both ends. Pass a CSS length to size it:
<Marquee fade="4rem">{/* ... */}</Marquee>The fade is a mask, not a gradient overlay. It works over an image, a gradient,
or any surface that is not a flat background colour.
Props
| Prop | Type | Default | Description |
|---|---|---|---|
vertical | boolean | false | Travel bottom to top instead of right to left. |
reverse | boolean | false | Flip the direction of travel. |
pauseOnHover | boolean | false | Stop while the pointer is over the track. |
repeat | number | 2 | Copies of the children. Two is the minimum. |
duration | number | 40 | Seconds for one full pass of a single copy. |
gap | string | "1rem" | Gap between items, as a CSS length. |
paused | boolean | false | Render the row static. |
fade | boolean | string | false | Fade both edges out. A string sizes it. |
className | string | none | Merged onto the track. |
Any other <div> prop is forwarded.
Why linear
A marquee is the one place linear is correct. Any easing curve makes the
loop visibly speed up and slow down once per pass, which points straight at
the seam you are trying to hide.
Accessibility
Motion stops under prefers-reduced-motion, leaving the first copy readable
in place. Duplicated copies are aria-hidden, so the content is announced
once.