Components
Marquee

Marquee

A continuous scrolling component for displaying content in a seamless loop.

Loading...

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-part attribute 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>
)

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>
)

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>
)

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>
)

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>
)

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>
)

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>
    </>
  )
}

If you're using the Marquee.RootProvider component, you don't need to use the Marquee.Root component.

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>
    </>
  )
}

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>
)

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 pauseOnInteraction to allow users to pause animations on hover or focus, improving accessibility and readability
  • Use descriptive labels: Provide meaningful aria-label values 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

Context

These are the properties available when using Marquee.Context, useMarqueeContext hook or useMarquee hook.

API

PropertyType
paused
boolean

Whether the marquee is currently paused.

orientation
"horizontal" | "vertical"

The current orientation of the marquee.

side
Side

The current side/direction of the marquee.

multiplier
number

The 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
number

The total number of content elements to render (original + clones). Use this value when rendering your content in a loop.

pause
VoidFunction

Pause the marquee animation.

resume
VoidFunction

Resume the marquee animation.

togglePause
VoidFunction

Toggle the pause state.

restart
VoidFunction

Restart the marquee animation from the beginning.

Accessibility

The Marquee component is built with accessibility in mind:

  • Uses appropriate ARIA attributes: role="region" and aria-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 pauseOnInteraction is enabled
  • Allows users to pause animations on hover or focus for better readability