---
import {
Marquee,
MarqueeContent,
MarqueeItem,
MarqueeToggle,
} from "@/components/ui/marquee"
const items = [
"Astro",
"Tailwind CSS",
"TypeScript",
"Content collections",
"Design tokens",
"Accessibility",
"Responsive images",
"View transitions",
"Markdown",
"Static output",
"Registries",
"Themes",
"Dark mode",
"Forms",
"Navigation",
"Search",
"Localization",
"Sitemaps",
"Performance",
"SEO",
]
---
<Marquee class="flex w-full flex-col items-end gap-4">
<MarqueeToggle playLabel="Play" pauseLabel="Pause" />
<MarqueeContent class="w-full mask-x-from-90%">
{items.map((item) => (
<MarqueeItem>
<span class="text-muted-foreground text-sm whitespace-nowrap">
{item}
</span>
</MarqueeItem>
))}
</MarqueeContent>
</Marquee>Installation
npx shadcn@latest add @fulldev/marquee
Usage
import {
Marquee,
MarqueeContent,
MarqueeItem,
MarqueeToggle,
} from "@/components/ui/marquee"
<Marquee class="w-full">
<MarqueeToggle playLabel="Play" pauseLabel="Pause" />
<MarqueeContent>
<MarqueeItem>Design systems</MarqueeItem>
<MarqueeItem>Astro</MarqueeItem>
<MarqueeItem>Content sites</MarqueeItem>
</MarqueeContent>
</Marquee>
Each item renders once. The row loops by moving its own items with Embla and its Auto Scroll plugin, so links and other content are never duplicated.
The row only moves when it can loop and the visitor allows motion. It stays a static, horizontally scrollable row when:
- the items do not fill the row,
- the visitor prefers reduced motion,
- the marquee has no enabled, visible
MarqueeToggle, because moving content needs a way to pause it. A row that moves stops when its toggle is later disabled, made inert or hidden, - JavaScript has not run.
MarqueeToggle shows only while the row moves. Its labels come from you, so they can be in the page language. Focus inside the row, dragging it or pressing the toggle pauses the motion until the toggle plays it again. Hovering with a mouse pauses it while the pointer is over the marquee.
Examples
Rows
A marquee can hold several MarqueeContent rows. One toggle controls all of them. Set reverse to move a row against the reading direction.
---
import {
Marquee,
MarqueeContent,
MarqueeItem,
MarqueeToggle,
} from "@/components/ui/marquee"
const languages = [
"TypeScript",
"JavaScript",
"Python",
"Go",
"Rust",
"Kotlin",
"Swift",
"Ruby",
"Elixir",
"PHP",
"C#",
"Java",
"Scala",
"Haskell",
"Dart",
"Zig",
"Lua",
"Perl",
"OCaml",
"Julia",
]
---
<Marquee class="flex w-full flex-col items-end gap-4">
<MarqueeToggle playLabel="Play" pauseLabel="Pause" />
<MarqueeContent class="w-full mask-x-from-90%">
{languages.map((language) => (
<MarqueeItem>
<span class="text-muted-foreground text-sm">{language}</span>
</MarqueeItem>
))}
</MarqueeContent>
<MarqueeContent reverse class="w-full mask-x-from-90%">
{languages.map((language) => (
<MarqueeItem>
<span class="text-muted-foreground text-sm">{language}</span>
</MarqueeItem>
))}
</MarqueeContent>
</Marquee>Few items
Items that fit the row stay centered and do not move.
---
import {
Marquee,
MarqueeContent,
MarqueeItem,
MarqueeToggle,
} from "@/components/ui/marquee"
---
<Marquee class="w-full">
<MarqueeToggle playLabel="Play" pauseLabel="Pause" />
<MarqueeContent>
<MarqueeItem>
<span class="text-muted-foreground text-sm">Tailwind CSS</span>
</MarqueeItem>
<MarqueeItem>
<span class="text-muted-foreground text-sm">PostCSS</span>
</MarqueeItem>
</MarqueeContent>
</Marquee>Styling
Items are --spacing(4) apart. Set --marquee-gap on the marquee to change that, for example class="[--marquee-gap:--spacing(12)]".
API Reference
See the GitHub source code for more information on props.