Marquee
A flexible scrolling layout for showcasing repeating content like logos, announcements, or testimonials.
Knockout
React
Angular
Vue
Svelte
Solid
Astro
Marko
Wiz
Qwik
Knockout
React
Angular
Vue
Svelte
Solid
Astro
Marko
Wiz
Qwik
1. Copy the component file
Create a new file called marquee.jsx in
your reusable components folder (for example
/src/components/) and paste the following code
into it.
marquee.jsx
import { memo } from "react";
import styles from "./marquee.module.css";
const Marquee = (props) => {
const {
children,
axis = "horizontal",
pauseOnHover = true,
reverse = false,
duration = 30,
repeat = 5,
mask = true,
className,
...restProps
} = props;
const _repeat = Math.max(1, repeat);
const _duration = Math.max(1, duration);
return (
<div
{...restProps}
className={[
className,
styles["marquee"],
styles[`axis-${axis}`],
reverse ? styles["reverse"] : "",
pauseOnHover ? styles["pause-on-hover"] : "",
mask ? styles["mask"] : "",
].join(" ")}
>
<div
className={styles["wrapper"]}
style={{
"--animation-duration": `${_duration}s`
}}
>
{Array.from({
length: _repeat,
}).map((_, index) => (
<div
key={`marquee-block-${index}`}
aria-hidden={index !== 0}
className={styles["batch"]}
>
{children}
</div>
))}
</div>
</div>
);
};
export default memo(Marquee); 2. Copy the CSS module file
In the same folder, create a file called marquee.module.css and paste the following CSS.
marquee.module.css
.marquee {
--gap: 16px;
--mask-breakpoint: 20%;
overflow: hidden;
>.wrapper {
display: flex;
gap: var(--gap);
width: max-content;
>.batch {
display: flex;
gap: var(--gap);
animation-duration: var(--animation-duration);
animation-timing-function: linear;
animation-iteration-count: infinite;
}
}
&.pause-on-hover:hover {
>.wrapper {
>.batch {
animation-play-state: paused;
}
}
}
&.reverse {
>.wrapper {
>.batch {
animation-direction: reverse;
}
}
}
&.axis-horizontal {
&.mask {
mask: linear-gradient(
to right,
transparent,
white var(--mask-breakpoint),
white calc(100% - var(--mask-breakpoint)),
transparent
);
}
>.wrapper {
flex-direction: row;
>.batch {
flex-direction: row;
animation-name: marquee-list-horizontal-keyframes;
}
}
}
&.axis-vertical {
&.mask {
mask: linear-gradient(
to bottom,
transparent,
white var(--mask-breakpoint),
white calc(100% - var(--mask-breakpoint)),
transparent
);
}
>.wrapper {
flex-direction: column;
>.batch {
flex-direction: column;
animation-name: marquee-list-vertical-keyframes;
}
}
}
}
@keyframes marquee-list-horizontal-keyframes {
from {
transform: translateX(0);
}
to {
transform: translateX(calc(-100% - var(--gap)));
}
}
@keyframes marquee-list-vertical-keyframes {
from {
transform: translateY(0);
}
to {
transform: translateY(calc(-100% - var(--gap)));
}
} 3. Use the component
Now you can import and use the component anywhere in your project.
marquee-preview.jsx
import Marquee from "@/components/essentials/marquee/marquee";
const MarqueePreview = () => {
const javaScriptLibAndFrameworks = ["Knockout", "React", "Angular", "Vue", "Svelte", "Solid", "Astro", "Marko", "Wiz", "Qwik"];
return (
<div style={{
width: "100%",
minWidth: "0",
padding: "32px",
}}>
<Marquee>
{javaScriptLibAndFrameworks.map(e => (
<Card key={e} name={e} />
))}
</Marquee>
<br />
<Marquee reverse>
{javaScriptLibAndFrameworks.map(e => (
<Card key={e} name={e} />
))}
</Marquee>
</div>
)
};
const Card = (props) => {
const {
name
} = props;
return (
<div
style={{
padding: "16px",
background: "var(--layer-tertiary)",
borderRadius: "4px",
}}
>
{name}
</div>
)
};
export default MarqueePreview; Vertical
Knockout
React
Angular
Vue
Svelte
Solid
Astro
Marko
Wiz
Qwik
Knockout
React
Angular
Vue
Svelte
Solid
Astro
Marko
Wiz
Qwik
marquee
import Marquee from "@/components/essentials/marquee/marquee";
/**
* When vertical axis is used, add max-height / height to container of Marquee.
*/
const MarqueeAxisPreview = () => {
const javaScriptLibAndFrameworks = ["Knockout", "React", "Angular", "Vue", "Svelte", "Solid", "Astro", "Marko", "Wiz", "Qwik"];
return (
<div style={{
display: "flex",
justifyContent: "center",
width: "100%",
minWidth: "0",
maxHeight: "320px", // <- This is important when vertical axis is used.
padding: "32px",
gap: "8px",
}}>
<Marquee
axis="vertical"
>
{javaScriptLibAndFrameworks.map(e => (
<Card key={e} name={e} />
))}
</Marquee>
<br />
<Marquee
axis="vertical"
reverse
>
{javaScriptLibAndFrameworks.map(e => (
<Card key={e} name={e} />
))}
</Marquee>
</div>
)
};
const Card = (props) => {
const {
name
} = props;
return (
<div
style={{
padding: "16px",
background: "var(--layer-tertiary)",
borderRadius: "4px",
}}
>
{name}
</div>
)
};
export default MarqueeAxisPreview; Duration
Knockout
React
Angular
Vue
Svelte
Solid
Astro
Marko
Wiz
Qwik
marquee
import Marquee from "@/components/essentials/marquee/marquee";
const MarqueePreview = () => {
const javaScriptLibAndFrameworks = ["Knockout", "React", "Angular", "Vue", "Svelte", "Solid", "Astro", "Marko", "Wiz", "Qwik"];
return (
<div style={{
width: "100%",
minWidth: "0",
padding: "32px",
}}>
<Marquee duration={5}>
{javaScriptLibAndFrameworks.map(e => (
<Card key={e} name={e} />
))}
</Marquee>
</div>
)
};
const Card = (props) => {
const {
name
} = props;
return (
<div
style={{
padding: "16px",
background: "var(--layer-tertiary)",
borderRadius: "4px",
}}
>
{name}
</div>
)
};
export default MarqueePreview; Props
Configure the component with the following props:
| Prop | Type | Required | Default | Description |
|---|---|---|---|---|
| children | React.ReactNode | Yes | — | Items to be displayed inside the marquee. These elements will scroll continuously. |
| axis | "horizontal" | "vertical" | No | "horizontal" | Controls the scrolling direction of the marquee. |
| pauseOnHover | boolean | No | true | Pauses the marquee animation when the user hovers over it. |
| reverse | boolean | No | false | Reverses the scrolling direction of the marquee animation. |
| duration | number | No | 30 | Duration of one animation cycle in seconds. Minimum value is 1. |
| repeat | number | No | 5 | Number of times the marquee content is repeated to maintain continuous scrolling. Increase this if the marquee items are small. |
| mask | boolean | No | true | Applies a fade mask at the beginning and end of the marquee. |
| className | string | No | — | Additional CSS class names applied to the marquee container. |
| style | React.CSSProperties | No | — | Inline styles applied to the marquee container. |