Marquee
A continuous scrolling component for displaying content in a seamless loop.
Features
- Smooth GPU-accelerated animations with seamless looping
- Horizontal and vertical scrolling with RTL support
- Auto-fill mode to duplicate content
- Customizable speed and spacing
- Pause on hover/focus with keyboard support
- Programmatic control and finite loops
Anatomy
To set up the marquee correctly, you'll need to understand its anatomy and how we name its parts.
Each part includes a
data-partattribute to help identify them in the DOM.
Examples
Learn how to use the Marquee component in your project. Let's take a look at the most basic example:
import { Marquee } from '@ark-ui/react/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const Basic = () => (
<Marquee.Root>
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '0 2rem' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
)
import { For } from 'solid-js'
import { Marquee } from '@ark-ui/solid/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const Basic = () => (
<Marquee.Root>
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '0 2rem' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
)
<script setup lang="ts">
import { Marquee } from '@ark-ui/vue/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<template>
<Marquee.Root>
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</template>
<script lang="ts">
import { Marquee } from '@ark-ui/svelte/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<Marquee.Root>
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 0 2rem">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
Auto Fill
Use the autoFill prop to automatically duplicate content to fill the viewport. The spacing prop controls the gap
between duplicated content instances:
import { Marquee } from '@ark-ui/react/marquee'
const items = ['Apple', 'Banana', 'Cherry']
export const AutoFill = () => (
<Marquee.Root autoFill spacing="2rem">
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '0 2rem' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
)
import { Marquee } from '@ark-ui/solid/marquee'
import { For } from 'solid-js'
const items = ['Apple', 'Banana', 'Cherry']
export const AutoFill = () => (
<Marquee.Root autoFill spacing="2rem">
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '0 2rem' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
)
<script setup lang="ts">
import { Marquee } from '@ark-ui/vue/marquee'
const items = ['Apple', 'Banana', 'Cherry']
</script>
<template>
<Marquee.Root auto-fill spacing="2rem">
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</template>
<script lang="ts">
import { Marquee } from '@ark-ui/svelte/marquee'
const items = ['Apple', 'Banana', 'Cherry']
</script>
<Marquee.Root autoFill spacing="2rem">
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 0 2rem">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
Reverse Direction
Set the reverse prop to reverse the scroll direction:
import { Marquee } from '@ark-ui/react/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const Reverse = () => (
<Marquee.Root reverse>
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '0 2rem' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
)
import { For } from 'solid-js'
import { Marquee } from '@ark-ui/solid/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const Reverse = () => (
<Marquee.Root reverse>
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '0 2rem' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
)
<script setup lang="ts">
import { Marquee } from '@ark-ui/vue/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<template>
<Marquee.Root reverse>
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</template>
<script lang="ts">
import { Marquee } from '@ark-ui/svelte/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<Marquee.Root reverse>
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 0 2rem">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
Vertical Orientation
Set side="bottom" (or side="top") to create a vertical marquee:
import { Marquee } from '@ark-ui/react/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const Vertical = () => (
<Marquee.Root side="bottom" style={{ height: '300px' }}>
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '1rem 0' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
)
import { For } from 'solid-js'
import { Marquee } from '@ark-ui/solid/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const Vertical = () => (
<Marquee.Root side="bottom" style={{ height: '300px' }}>
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '1rem 0' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
)
<script setup lang="ts">
import { Marquee } from '@ark-ui/vue/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<template>
<Marquee.Root side="bottom" style="height: 300px">
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 1rem 0">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</template>
<script lang="ts">
import { Marquee } from '@ark-ui/svelte/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<Marquee.Root side="bottom" style="height: 300px">
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 1rem 0">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
Custom Speed
Control the animation speed using the speed prop, which accepts values in pixels per second:
import { Marquee } from '@ark-ui/react/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const Speed = () => (
<div style={{ display: 'flex', flexDirection: 'column', gap: '2rem' }}>
<div>
<h3>Slow (25px/s)</h3>
<Marquee.Root speed={25}>
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '0 2rem' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
<div>
<h3>Normal (50px/s)</h3>
<Marquee.Root speed={50}>
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '0 2rem' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
<div>
<h3>Fast (100px/s)</h3>
<Marquee.Root speed={100}>
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '0 2rem' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
</div>
)
import { For } from 'solid-js'
import { Marquee } from '@ark-ui/solid/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const Speed = () => (
<div style={{ display: 'flex', 'flex-direction': 'column', gap: '2rem' }}>
<div>
<h3>Slow (25px/s)</h3>
<Marquee.Root speed={25}>
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '0 2rem' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
<div>
<h3>Normal (50px/s)</h3>
<Marquee.Root speed={50}>
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '0 2rem' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
<div>
<h3>Fast (100px/s)</h3>
<Marquee.Root speed={100}>
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '0 2rem' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
</div>
)
<script setup lang="ts">
import { Marquee } from '@ark-ui/vue/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<template>
<div style="display: flex; flex-direction: column; gap: 2rem">
<div>
<h3>Slow (25px/s)</h3>
<Marquee.Root :speed="25">
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
<div>
<h3>Normal (50px/s)</h3>
<Marquee.Root :speed="50">
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
<div>
<h3>Fast (100px/s)</h3>
<Marquee.Root :speed="100">
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
</div>
</template>
<script lang="ts">
import { Marquee } from '@ark-ui/svelte/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<div style="display: flex; flex-direction: column; gap: 2rem">
<div>
<h3>Slow (25px/s)</h3>
<Marquee.Root speed={25}>
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 0 2rem">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
<div>
<h3>Normal (50px/s)</h3>
<Marquee.Root speed={50}>
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 0 2rem">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
<div>
<h3>Fast (100px/s)</h3>
<Marquee.Root speed={100}>
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 0 2rem">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</div>
</div>
Pause on Interaction
Enable pauseOnInteraction to pause the marquee when users hover or focus on it, improving accessibility:
import { Marquee } from '@ark-ui/react/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const PauseOnInteraction = () => (
<Marquee.Root pauseOnInteraction>
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '0 2rem' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
)
import { For } from 'solid-js'
import { Marquee } from '@ark-ui/solid/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const PauseOnInteraction = () => (
<Marquee.Root pauseOnInteraction>
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '0 2rem' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
)
<script setup lang="ts">
import { Marquee } from '@ark-ui/vue/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<template>
<Marquee.Root pause-on-interaction>
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
</template>
<script lang="ts">
import { Marquee } from '@ark-ui/svelte/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<Marquee.Root pauseOnInteraction>
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 0 2rem">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
Programmatic Control
Use the useMarquee hook with Marquee.RootProvider to access the marquee API and control playback programmatically:
import { Marquee, useMarquee } from '@ark-ui/react/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const ProgrammaticControl = () => {
const marquee = useMarquee()
return (
<>
<Marquee.RootProvider value={marquee}>
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '0 2rem' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.RootProvider>
<div style={{ marginTop: '1rem', display: 'flex', gap: '0.5rem' }}>
<button onClick={() => marquee.pause()}>Pause</button>
<button onClick={() => marquee.resume()}>Resume</button>
</div>
</>
)
}
import { For } from 'solid-js'
import { Marquee, useMarquee } from '@ark-ui/solid/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const ProgrammaticControl = () => {
const marquee = useMarquee()
return (
<>
<Marquee.RootProvider value={marquee}>
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '0 2rem' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.RootProvider>
<div style={{ 'margin-top': '1rem', display: 'flex', gap: '0.5rem' }}>
<button onClick={() => marquee().pause()}>Pause</button>
<button onClick={() => marquee().resume()}>Resume</button>
</div>
</>
)
}
<script setup lang="ts">
import { Marquee, useMarquee } from '@ark-ui/vue/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
const marquee = useMarquee({})
</script>
<template>
<Marquee.RootProvider :value="marquee">
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.RootProvider>
<div style="margin-top: 1rem; display: flex; gap: 0.5rem">
<button @click="marquee.pause()">Pause</button>
<button @click="marquee.resume()">Resume</button>
</div>
</template>
<script lang="ts">
import { Marquee, useMarquee } from '@ark-ui/svelte/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
const id = $props.id()
const marquee = useMarquee(() => ({ id }))
</script>
<Marquee.RootProvider value={marquee}>
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 0 2rem">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.RootProvider>
<div style="margin-top: 1rem; display: flex; gap: 0.5rem">
<button onclick={() => marquee().pause()}>Pause</button>
<button onclick={() => marquee().resume()}>Resume</button>
</div>
If you're using the
Marquee.RootProvidercomponent, you don't need to use theMarquee.Rootcomponent.
Finite Loops
Set the loopCount prop to run the marquee a specific number of times. Use onLoopComplete to track each loop
iteration and onComplete to know when all loops finish:
import { useState } from 'react'
import { Marquee } from '@ark-ui/react/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const FiniteLoops = () => {
const [loopCount, setLoopCount] = useState(0)
const [completedCount, setCompletedCount] = useState(0)
return (
<>
<Marquee.Root
loopCount={3}
onLoopComplete={() => setLoopCount((prev) => prev + 1)}
onComplete={() => setCompletedCount((prev) => prev + 1)}
>
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '0 2rem' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
<div style={{ marginTop: '1rem' }}>
<p>Loop completed: {loopCount} times</p>
<p>Animation completed: {completedCount} times</p>
</div>
</>
)
}
import { For, createSignal } from 'solid-js'
import { Marquee } from '@ark-ui/solid/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const FiniteLoops = () => {
const [loopCount, setLoopCount] = createSignal(0)
const [completedCount, setCompletedCount] = createSignal(0)
return (
<>
<Marquee.Root
loopCount={3}
onLoopComplete={() => setLoopCount((prev) => prev + 1)}
onComplete={() => setCompletedCount((prev) => prev + 1)}
>
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '0 2rem' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
<div style={{ 'margin-top': '1rem' }}>
<p>Loop completed: {loopCount()} times</p>
<p>Animation completed: {completedCount()} times</p>
</div>
</>
)
}
<script setup lang="ts">
import { ref } from 'vue'
import { Marquee } from '@ark-ui/vue/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
const loopCount = ref(0)
const completedCount = ref(0)
</script>
<template>
<Marquee.Root :loop-count="3" @loop-complete="loopCount++" @complete="completedCount++">
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
<div style="margin-top: 1rem">
<p>Loop completed: {{ loopCount }} times</p>
<p>Animation completed: {{ completedCount }} times</p>
</div>
</template>
<script lang="ts">
import { Marquee } from '@ark-ui/svelte/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
let loopCount = $state(0)
let completedCount = $state(0)
</script>
<Marquee.Root
loopCount={3}
onLoopComplete={() => loopCount++}
onComplete={() => completedCount++}
>
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 0 2rem">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
</Marquee.Root>
<div style="margin-top: 1rem">
<p>Loop completed: {loopCount} times</p>
<p>Animation completed: {completedCount} times</p>
</div>
Edge Gradients
Add Marquee.Edge components to create fade effects at the start and end of the scrolling area:
import { Marquee } from '@ark-ui/react/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const WithEdges = () => (
<Marquee.Root>
<Marquee.Edge side="start" />
<Marquee.Viewport>
<Marquee.Content>
{items.map((item, i) => (
<Marquee.Item key={i} style={{ padding: '0 2rem' }}>
{item}
</Marquee.Item>
))}
</Marquee.Content>
</Marquee.Viewport>
<Marquee.Edge side="end" />
</Marquee.Root>
)
import { For } from 'solid-js'
import { Marquee } from '@ark-ui/solid/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
export const WithEdges = () => (
<Marquee.Root>
<Marquee.Edge side="start" />
<Marquee.Viewport>
<Marquee.Content>
<For each={items}>{(item) => <Marquee.Item style={{ padding: '0 2rem' }}>{item}</Marquee.Item>}</For>
</Marquee.Content>
</Marquee.Viewport>
<Marquee.Edge side="end" />
</Marquee.Root>
)
<script setup lang="ts">
import { Marquee } from '@ark-ui/vue/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<template>
<Marquee.Root>
<Marquee.Edge side="start" />
<Marquee.Viewport>
<Marquee.Content>
<Marquee.Item v-for="item in items" :key="item" style="padding: 0 2rem">{{ item }}</Marquee.Item>
</Marquee.Content>
</Marquee.Viewport>
<Marquee.Edge side="end" />
</Marquee.Root>
</template>
<script lang="ts">
import { Marquee } from '@ark-ui/svelte/marquee'
const items = ['Apple', 'Banana', 'Cherry', 'Date', 'Elderberry', 'Fig', 'Grape']
</script>
<Marquee.Root>
<Marquee.Edge side="start" />
<Marquee.Viewport>
<Marquee.Content>
{#each items as item}
<Marquee.Item style="padding: 0 2rem">{item}</Marquee.Item>
{/each}
</Marquee.Content>
</Marquee.Viewport>
<Marquee.Edge side="end" />
</Marquee.Root>
Guides
Styling Requirements
The Marquee component requires CSS keyframe animations to function properly. You'll need to define animations for both horizontal and vertical scrolling:
@keyframes marqueeX {
from {
transform: translateX(0);
}
to {
transform: translateX(var(--marquee-translate));
}
}
@keyframes marqueeY {
from {
transform: translateY(0);
}
to {
transform: translateY(var(--marquee-translate));
}
}
The component automatically applies the appropriate animation (marqueeX or marqueeY) based on the scroll direction
and uses the --marquee-translate CSS variable for seamless looping.
You can target specific parts of the marquee using data-part attributes for custom styling:
[data-part="root"]- The root container[data-part="viewport"]- The scrolling viewport[data-part="content"]- The content wrapper (receives animation)[data-part="item"]- Individual marquee items[data-part="edge"]- Edge gradient overlays
Best Practices
- Enable pause-on-interaction: Use
pauseOnInteractionto allow users to pause animations on hover or focus, improving accessibility and readability - Use descriptive labels: Provide meaningful
aria-labelvalues that describe the marquee content (e.g., "Partner logos", "Latest announcements") - Avoid for critical information: Don't use marquees for essential content that users must read, as continuously moving text can be difficult to process. Consider static displays for important information
API Reference
Props
Root
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. | |
autoFill | false | booleanWhether to automatically duplicate content to fill the container. |
defaultPaused | false | booleanWhether the marquee is paused by default. |
delay | 0 | numberThe delay before the animation starts (in seconds). |
ids | Partial<{ root: string; viewport: string; content: (index: number) => string }>The ids of the elements in the marquee. Useful for composition. | |
loopCount | 0 | numberThe number of times to loop the animation (0 = infinite). |
onComplete | () => voidFunction called when the marquee completes all loops and stops. Only fires for finite loops (loopCount > 0). | |
onLoopComplete | () => voidFunction called when the marquee completes one loop iteration. | |
onPauseChange | (details: PauseStatusDetails) => voidFunction called when the pause status changes. | |
paused | booleanWhether the marquee is paused. | |
pauseOnInteraction | false | booleanWhether to pause the marquee on user interaction (hover, focus). |
reverse | false | booleanWhether to reverse the animation direction. |
side | 'start' | SideThe side/direction the marquee scrolls towards. |
spacing | '1rem' | stringThe spacing between marquee items. |
speed | 50 | numberThe speed of the marquee animation in pixels per second. |
translations | IntlTranslationsThe localized messages to use. |
| CSS Variable | Description |
|---|---|
--marquee-duration | The marquee duration value for the Root |
--marquee-spacing | The marquee spacing value for the Root |
--marquee-delay | The marquee delay value for the Root |
--marquee-loop-count | The marquee loop count value for the Root |
--marquee-translate | The marquee translate value for the Root |
| Data Attribute | Value |
|---|---|
[data-scope] | marquee |
[data-part] | root |
[data-state] | "paused" | "idle" |
[data-orientation] | The orientation of the marquee |
[data-paused] | Present when paused |
Content
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | marquee |
[data-part] | |
[data-index] | The index of the item |
[data-orientation] | The orientation of the content |
[data-side] | |
[data-reverse] | |
[data-clone] |
Edge
| Prop | Default | Type |
|---|---|---|
side | SideThe side where the edge gradient should appear. | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | marquee |
[data-part] | |
[data-side] | |
[data-orientation] | The orientation of the edge |
Item
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
RootProvider
| Prop | Default | Type |
|---|---|---|
value | UseMarqueeReturn | |
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
Viewport
| Prop | Default | Type |
|---|---|---|
asChild | booleanUse the provided child element as the default rendered element, combining their props and behavior. For more details, read our Composition guide. |
| Data Attribute | Value |
|---|---|
[data-scope] | marquee |
[data-part] | |
[data-orientation] | The orientation of the viewport |
[data-side] |
Context
These are the properties available when using Marquee.Context, useMarqueeContext hook or useMarquee hook.
API
| Property | Type |
|---|---|
paused | booleanWhether the marquee is currently paused. |
orientation | "horizontal" | "vertical"The current orientation of the marquee. |
side | SideThe current side/direction of the marquee. |
multiplier | numberThe multiplier for auto-fill. Indicates how many times to duplicate content. When autoFill is enabled and content is smaller than container, this returns the number of additional copies needed. Otherwise returns 1. |
contentCount | numberThe total number of content elements to render (original + clones). Use this value when rendering your content in a loop. |
pause | VoidFunctionPause the marquee animation. |
resume | VoidFunctionResume the marquee animation. |
togglePause | VoidFunctionToggle the pause state. |
restart | VoidFunctionRestart the marquee animation from the beginning. |
Accessibility
The Marquee component is built with accessibility in mind:
- Uses appropriate ARIA attributes:
role="region"andaria-roledescription="marquee" - Cloned content for seamless looping is marked with
aria-hidden="true"to avoid confusion for screen readers - Automatically respects user motion preferences via
prefers-reduced-motion - Supports keyboard interaction when
pauseOnInteractionis enabled - Allows users to pause animations on hover or focus for better readability