Mantine Marquee

Logo

@gfazioli/mantine-marquee

Mantine component for seamless, GPU-accelerated infinite-scrolling loops — a drop-in superset of the core Marquee with responsive props, masked or colored fade edges, a keyboard-focus pause, and 3D isometric and circle variants.

Upgrading from v4? v5 resolves gap through theme.spacing, like core. See the Upgrade guide for the size table and the other breaking changes.

Installation

yarn add @gfazioli/mantine-marquee

After installation import package styles at the root of your application:

import '@gfazioli/mantine-marquee/styles.css';

You can import styles within a layer @layer mantine-marquee by importing @gfazioli/mantine-marquee/styles.layer.css file.

import '@gfazioli/mantine-marquee/styles.layer.css';

Comparison with Mantine core Marquee

Since version 9.0, Mantine core ships a first-party Marquee component. The basics overlap — a seamless loop of repeated children, horizontal or vertical, reverse and pause on hover — but the two make different trade-offs.

CapabilityMarqueeCore Marquee

3D variants

variant="isometric" lays the loop on a tilted plane (tilt, perspective, rotate, skew); variant="circle" spins the children around a 3D ring (radius, tilt)

Fade edges over any background

By default Marquee fades with a CSS mask (true alpha), so it works over images and gradients — core always paints a gradient in fadeEdgeColor, which has to match the background

Fade edge shapes

linear, ellipse or rect, and rect takes a separate size per axis ([x, y]) — core fades the two scroll edges

Responsive orientation and gap

vertical and gap accept Mantine breakpoint maps, e.g. vertical on mobile and horizontal from md

Reduced motion

With prefers-reduced-motion: reduce the scroll stops and the items stay readable — core keeps animating

Pause on keyboard focus

With pauseOnHover, the loop also stops while a link or button inside has keyboard focus, so it does not scroll away from the user — core pauses on hover only

Theme colors for the fade

fadeEdgeColor takes a theme key (blue, blue.3, dark.7) as well as any CSS color, and paints every fade shape — core passes the string to CSS as it is, so red means the CSS named color

Horizontal and vertical scrolling

Both: orientation="vertical" in core; vertical in Marquee, which also accepts orientation as an alias

Reverse, pause on hover, gap and repeat

Same props, different defaults — repeat 2 here, 4 in core; duration 20 s here, 100000 ms (100 s) in core. gap takes the same values in both: a theme.spacing key (md by default), a number or any CSS value

Styles API

Both: root, content, group, with classNames, styles, vars and attributes, and the data-orientation, data-reverse and data-pause-on-hover attributes on the root. Marquee adds stage, plane, tilt, ring and item for the 3D variants

Custom fade color

fadeEdgeColor paints the fade edges in a color, in both

When to use which. Reach for the core Marquee for a plain logo or text strip on a solid background, with nothing extra to install. Reach for Marquee when the strip sits over an image or a gradient, when you want a 3D isometric plane or a rotating ring, when the layout changes at a breakpoint, or when the motion should stop for users who ask for less of it or who reach it with the keyboard. Moving from core is a matter of changing the import: see Drop-in for Mantine core Marquee.

Usage

The Marquee component allows you to create a marquee effect with any content.

Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #5
Hope you like it #6
Have a nice day #7
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #5
Hope you like it #6
Have a nice day #7
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #5
Hope you like it #6
Have a nice day #7
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #5
Hope you like it #6
Have a nice day #7
Repeat
Duration
Gap
Fade edges size
Fade edges size y
import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { Box } from '@mantine/core';

function Demo() {
  function BoxComponent({ children, ...props }: { children: ReactNode; [key: string]: any }) {
    return (
      <Box {...props} p="md" w="200px" c="white" style={{ borderRadius: '8px' }}>
        {children}
      </Box>
    );
  }

  return (
    <Marquee repeat={4} duration={10} gap="sm" fadeEdgesSize={20}>
      <BoxComponent bg="red">Hello World #1</BoxComponent>
      <BoxComponent bg="cyan">Hope you like it #2</BoxComponent>
      <BoxComponent bg="blue">Have a nice day #3</BoxComponent>
      <BoxComponent bg="lime">Goodbye #4</BoxComponent>
    </Marquee>
  );
}

Fade edges

The fadeEdges prop adds a smooth fade effect at the edges of the scroll area using CSS mask-image. It accepts true, "linear", "ellipse", or "rect" — passing true is equivalent to "linear". Use fadeEdgesSize to control how much of the edge is faded ("xs" | "sm" | "md" | "lg" | "xl" or a percentage string).

Linear

fadeEdges="linear" applies a gradient mask on the leading and trailing scroll edges. This works for both horizontal and vertical layouts, and it is ideal for content that enters and exits from the sides.

Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { Box, Flex } from '@mantine/core';

function Demo() {
  function BoxComponent({ children, ...props }: { children: ReactNode; [key: string]: any }) {
    return (
      <Box {...props} p="md" w="200px" c="white" style={{ borderRadius: '8px' }}>
        {children}
      </Box>
    );
  }

  return (
    <Flex direction="column" gap="md">
      <Marquee fadeEdges="linear" fadeEdgesSize="sm">
        <BoxComponent bg="blue">Hello World #1</BoxComponent>
        <BoxComponent bg="cyan">Hope you like it #2</BoxComponent>
        <BoxComponent bg="indigo">Have a nice day #3</BoxComponent>
        <BoxComponent bg="teal">Goodbye #4</BoxComponent>
      </Marquee>
      <Marquee fadeEdges="linear" fadeEdgesSize="sm" reverse>
        <BoxComponent bg="orange">Hello World #1</BoxComponent>
        <BoxComponent bg="red">Hope you like it #2</BoxComponent>
        <BoxComponent bg="pink">Have a nice day #3</BoxComponent>
        <BoxComponent bg="grape">Goodbye #4</BoxComponent>
      </Marquee>
    </Flex>
  );
}

Ellipse

fadeEdges="ellipse" applies a radial elliptical mask that fades all around the border. This is orientation-independent — it works identically for horizontal and vertical marquees. It is particularly effective for logo carousels, icon showcases, or any content where a vignette effect is desired.

import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { ThemeIcon } from '@mantine/core';
import {
  IconBrand4chan,
  IconBrandAmazon,
  IconBrandBing,
  IconBrandGithub,
  IconBrandMantine,
  IconBrandWhatsapp,
  IconBrandWordpress,
} from '@tabler/icons-react';

function Demo() {
  const iconsBrand = [
    <IconBrand4chan style={{ width: '70%', height: '70%' }} />,
    <IconBrandWhatsapp style={{ width: '70%', height: '70%' }} />,
    <IconBrandWordpress style={{ width: '70%', height: '70%' }} />,
    <IconBrandBing style={{ width: '70%', height: '70%' }} />,
    <IconBrandGithub style={{ width: '70%', height: '70%' }} />,
    <IconBrandMantine style={{ width: '70%', height: '70%' }} />,
    <IconBrandAmazon style={{ width: '70%', height: '70%' }} />,
  ];

  function BoxComponent({ children, ...props }: { children: ReactNode; [key: string]: any }) {
    return (
      <ThemeIcon {...props} variant="transparent" size="120px">
        {children}
      </ThemeIcon>
    );
  }

  return (
    <Marquee pauseOnHover fadeEdges="ellipse" fadeEdgesSize="md">
      {iconsBrand.map((icon, index) => (
        <BoxComponent key={index}>{icon}</BoxComponent>
      ))}
    </Marquee>
  );
}

Circle

Since the ellipse mask uses radial-gradient(ellipse closest-side), applying fadeEdges="ellipse" to a square element (w and h are equal) produces a perfectly circular mask. No additional prop is needed — the geometry does the work.

John Doe

Jane Doe

Jessica Doe

Jack Doe

Jill Doe

John Doe

Jane Doe

Jessica Doe

Jack Doe

Jill Doe

import { Marquee } from '@gfazioli/mantine-marquee';
import { Avatar, Card, Center, Rating, Stack, Text } from '@mantine/core';

function Demo() {
  const testimonials = [
    { name: 'John Doe', avatar: '...', rating: 4 },
    { name: 'Jane Doe', avatar: '...', rating: 4 },
    { name: 'Jessica Doe', avatar: '...', rating: 2 },
    { name: 'Jack Doe', avatar: '...', rating: 5 },
    { name: 'Jill Doe', avatar: '...', rating: 3 },
  ];

  return (
    <Center>
      {/* Ellipse on a square element (w=h) produces a circular mask */}
      <Marquee
        w={300}
        h={300}
        pauseOnHover
        fadeEdges="ellipse"
        fadeEdgesSize="md"
        style={{ justifyContent: 'center' }}
      >
        {testimonials.map((t, index) => (
          <Card key={index} w={120} shadow="sm" padding="sm" radius="md" withBorder>
            <Stack align="center" gap="xs">
              <Avatar size="lg" radius="xl" src={t.avatar} />
              <Text size="xs" fw={600} ta="center">
                {t.name}
              </Text>
              <Rating value={t.rating} size="xs" />
            </Stack>
          </Card>
        ))}
      </Marquee>
    </Center>
  );
}

Rect

fadeEdges="rect" uses two intersected linear gradients (mask-composite: intersect) to fade all 4 edges independently. The horizontal gradient controls left/right, the vertical gradient controls top/bottom — at the corners the alpha values multiply naturally, creating a smooth diagonal falloff.

fadeEdgesSize accepts a [horizontal, vertical] tuple for independent axis control. For example fadeEdgesSize={['lg', 'xs']} applies a large fade on left/right and a small fade on top/bottom.

Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { Box, Flex } from '@mantine/core';

function Demo() {
  function BoxComponent({ children, ...props }: { children: ReactNode; [key: string]: any }) {
    return (
      <Box {...props} p="md" w="200px" c="white" style={{ borderRadius: '8px' }}>
        {children}
      </Box>
    );
  }

  return (
    <Flex direction="column" gap="md">
      {/* Uniform fade on all 4 edges */}
      <Marquee fadeEdges="rect" fadeEdgesSize="md" h={60}>
        <BoxComponent bg="blue">Hello World #1</BoxComponent>
        <BoxComponent bg="cyan">Hope you like it #2</BoxComponent>
        <BoxComponent bg="indigo">Have a nice day #3</BoxComponent>
        <BoxComponent bg="teal">Goodbye #4</BoxComponent>
      </Marquee>
      {/* Tuple: more fade on left/right (lg), less on top/bottom (xs) */}
      <Marquee fadeEdges="rect" fadeEdgesSize={['lg', 'xs']} reverse h={60}>
        <BoxComponent bg="orange">Hello World #1</BoxComponent>
        <BoxComponent bg="red">Hope you like it #2</BoxComponent>
        <BoxComponent bg="pink">Have a nice day #3</BoxComponent>
        <BoxComponent bg="grape">Goodbye #4</BoxComponent>
      </Marquee>
    </Flex>
  );
}

Fade color

By default the fade is a mask: the content fades to transparent, into whatever is behind the marquee, so it works over images and gradients. Set fadeEdgeColor to paint the edges in a color instead. It takes a theme color key ("blue", "blue.3", "dark.7") or any CSS color, and works with every shape and variant; fadeEdges still turns the fade on. Use it to fade into a brand color that differs from the background, or to reproduce the core Marquee look with fadeEdgeColor="var(--mantine-color-body)".

Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #5
Hope you like it #6
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #5
Hope you like it #6
Fade edge color
import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { Box } from '@mantine/core';

function BoxComponent({ children, ...props }: { children: ReactNode; [key: string]: any }) {
  return (
    <Box {...props} p="md" w="200px" c="white" style={{ borderRadius: '8px' }}>
      {children}
    </Box>
  );
}

function Demo() {
  return (
    <Marquee fadeEdges="linear" fadeEdgeColor="blue" fadeEdgesSize="md" maw={540}>
      <BoxComponent bg="red">Hello World #1</BoxComponent>
      <BoxComponent bg="cyan">Hope you like it #2</BoxComponent>
      <BoxComponent bg="blue">Have a nice day #3</BoxComponent>
      <BoxComponent bg="lime">Goodbye #4</BoxComponent>
      <BoxComponent bg="orange">Hello World #5</BoxComponent>
      <BoxComponent bg="grape">Hope you like it #6</BoxComponent>
    </Marquee>
  );
}

Multiple

The Marquee component allows you to create a marquee effect with multiple items.

Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { Box } from '@mantine/core';

function Demo() {
  function BoxComponent({ children, ...props }: { children: ReactNode; [key: string]: any }) {
    return (
      <Box {...props} p="md" w="200px" c="white" style={{ borderRadius: '8px' }}>
        {children}
      </Box>
    );
  }

  return (
    <>
      <Marquee mb={16} fadeEdges>
        <BoxComponent bg="red">Hello World #1</BoxComponent>
        <BoxComponent bg="cyan">Hope you like it #2</BoxComponent>
        <BoxComponent bg="blue">Have a nice day #3</BoxComponent>
        <BoxComponent bg="lime">Goodbye #4</BoxComponent>
      </Marquee>
      <Marquee reverse fadeEdges>
        <BoxComponent bg="red">Hello World #1</BoxComponent>
        <BoxComponent bg="cyan">Hope you like it #2</BoxComponent>
        <BoxComponent bg="blue">Have a nice day #3</BoxComponent>
        <BoxComponent bg="lime">Goodbye #4</BoxComponent>
      </Marquee>
    </>
  );
}

Vertical

The Marquee component allows you to create a marquee effect with multiple items in vertical direction.

Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { Box, Flex } from '@mantine/core';

function Demo() {
  function BoxComponent({ children, ...props }: { children: ReactNode; [key: string]: any }) {
    return (
      <Box {...props} p="md" w="200px" c="white" style={{ borderRadius: '8px' }}>
        {children}
      </Box>
    );
  }

  return (
    <Flex w={500}>
      <Marquee h={300} fadeEdges vertical>
        <BoxComponent bg="red">Hello World #1</BoxComponent>
        <BoxComponent bg="cyan">Hope you like it #2</BoxComponent>
        <BoxComponent bg="blue">Have a nice day #3</BoxComponent>
        <BoxComponent bg="lime">Goodbye #4</BoxComponent>
      </Marquee>
      <Marquee h={300} reverse fadeEdges vertical>
        <BoxComponent bg="red">Hello World #1</BoxComponent>
        <BoxComponent bg="cyan">Hope you like it #2</BoxComponent>
        <BoxComponent bg="blue">Have a nice day #3</BoxComponent>
        <BoxComponent bg="lime">Goodbye #4</BoxComponent>
      </Marquee>
    </Flex>
  );
}

Pause on hover and focus

pauseOnHover stops the loop while the pointer is over the marquee, and also while a link or a button inside it has keyboard focus, so a keyboard user can stop on an item instead of chasing it. Press Tab to move into the demo below. The pause is pure CSS (:hover and :focus-visible): it costs no re-render and works before hydration, in all three variants.

import { Marquee } from '@gfazioli/mantine-marquee';
import { Anchor, Paper } from '@mantine/core';

const links = ['Mantine', 'Marquee', 'Split Pane', 'Window', 'Onboarding Tour', 'Json Tree'];

function Demo() {
  return (
    <Marquee pauseOnHover fadeEdges duration={30}>
      {links.map((label) => (
        <Paper key={label} withBorder radius="md" px="lg" py="sm">
          <Anchor href="#pause-on-hover-and-focus">{label}</Anchor>
        </Paper>
      ))}
    </Marquee>
  );
}

Isometric variant

Set variant="isometric" to lay the marquee on a plane tilted back in 3D space — content scrolls across a surface, as if the items were resting on a table. The existing scroll loop is reused unchanged, so every other prop (reverse, duration, pauseOnHover, gap, vertical) keeps working.

Use tilt to control the plane inclination in degrees and perspective to control the depth in pixels (smaller values exaggerate the effect). Add rotate (in-plane rotateZ) and skew (horizontal skewX) to dial in a classic ¾ isometric (2.5D) look. The 3D scene is composited as a flat image on the root element, so fadeEdges masks still apply on top of the tilted plane — the linear fade automatically follows the projected scroll axis, so it keeps fading the leading and trailing items even when the plane is rotated.

Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #5
Hope you like it #6
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #5
Hope you like it #6
Tilt
Perspective
Rotate
Skew
Fade edges size
Duration
Gap
import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { Box } from '@mantine/core';

function Demo() {
  function Card({ children, ...rest }: { children: ReactNode; [key: string]: any }) {
    return (
      <Box {...rest} p="md" w={200} h={120} c="white" style={{ borderRadius: '8px' }}>
        {children}
      </Box>
    );
  }

  return (
    <Marquee variant="isometric" tilt={48} perspective={750} fadeEdgesSize={15} duration={12} gap="xs">
      <Card bg="red">Hello World #1</Card>
      <Card bg="cyan">Hope you like it #2</Card>
      <Card bg="blue">Have a nice day #3</Card>
      <Card bg="lime">Goodbye #4</Card>
      <Card bg="orange">Hello World #5</Card>
      <Card bg="grape">Hope you like it #6</Card>
    </Marquee>
  );
}

Circle variant

Set variant="circle" to distribute the children around a 3D ellipse ring that rotates continuously, like a carousel. Each child is positioned once around the ring (no clones) and the whole ring spins — rotateY(360deg) equals rotateY(0deg), so the loop is perfectly seamless.

The items are mounted on the rim facing outward, so the ring reads as a 3D carousel/cylinder. Use radius to size the ring (a single number for a circular ring, or a [rx, ry] tuple for an explicit ellipse), tilt for the viewing angle (positive views the ring from above, negative from below), and perspective for the depth. reverse, duration and pauseOnHover work as usual; repeat, gap and vertical do not apply to a ring.

The circle variant needs a height — set h (or mih) on the component.

Radius
Tilt
Perspective
Duration
import { Marquee } from '@gfazioli/mantine-marquee';
import { Box, Flex } from '@mantine/core';

const SITES = [
  { label: 'Findergit', href: 'https://www.findergit.app/', bg: 'red' },
  { label: 'Netfox', href: 'https://netfox.app', bg: 'cyan' },
  { label: 'Octoscope', href: 'https://gfazioli.github.io/octoscope/', bg: 'blue' },
  { label: 'MEH', href: 'https://mantine-extensions.vercel.app', bg: 'grape' },
  { label: 'Clock', href: 'https://gfazioli.github.io/mantine-clock', bg: 'lime' },
  { label: 'Scene', href: 'https://gfazioli.github.io/mantine-scene', bg: 'teal' },
  { label: 'Book', href: 'https://gfazioli.github.io/mantine-book', bg: 'orange' },
  { label: 'Marquee', href: 'https://gfazioli.github.io/mantine-marquee', bg: 'pink' },
];

const cardCss = `
  .circle-card {
    transition: transform 160ms ease, box-shadow 160ms ease, filter 160ms ease;
  }
  .circle-card:hover {
    transform: scale(1.12);
    filter: brightness(1.12);
    box-shadow: 0 10px 30px rgba(0, 0, 0, 0.5);
  }
`;

function Demo() {
  return (
    <>
      <style>{cardCss}</style>
      <Flex w="100%" h={440} align="center" justify="center">
        <Marquee variant="circle" w="100%" h={440} radius={250} tilt={-15} perspective={700} duration={16} pauseOnHover>
          {SITES.map((site) => (
            <Box
              key={site.label}
              component="a"
              href={site.href}
              target="_blank"
              rel="noreferrer"
              className="circle-card"
              bg={site.bg}
              w={150}
              h={90}
              c="white"
              fw={600}
              style={{ display: 'grid', placeItems: 'center', borderRadius: '8px', textDecoration: 'none' }}
            >
              {site.label}
            </Box>
          ))}
        </Marquee>
      </Flex>
    </>
  );
}

Responsive vertical

The vertical prop accepts a responsive breakpoint object in addition to a plain boolean. This lets you switch between vertical and horizontal layouts at different viewport widths using Mantine's useMatches hook under the hood.

// Vertical on mobile, horizontal from md breakpoint up
<Marquee vertical={{ base: true, md: false }}>{/* ... */}</Marquee>
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { Box } from '@mantine/core';

function Demo() {
  function BoxComponent({ children, ...props }: { children: ReactNode; [key: string]: any }) {
    return (
      <Box {...props} p="md" w="200px" c="white" style={{ borderRadius: '8px' }}>
        {children}
      </Box>
    );
  }

  return (
    <Marquee vertical={{ base: true, md: false }} h={300} fadeEdges>
      <BoxComponent bg="blue">Hello World #1</BoxComponent>
      <BoxComponent bg="cyan">Hope you like it #2</BoxComponent>
      <BoxComponent bg="indigo">Have a nice day #3</BoxComponent>
      <BoxComponent bg="teal">Goodbye #4</BoxComponent>
    </Marquee>
  );
}

Responsive gap

The gap prop also accepts a responsive breakpoint object. This is useful when switching layout direction at a breakpoint — you can pair it with a different gap size.

// Small gap on mobile, large gap from md breakpoint up
<Marquee gap={{ base: 'xs', md: 'xl' }}>{/* ... */}</Marquee>
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
Hello World #1
Hope you like it #2
Have a nice day #3
Goodbye #4
import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { Box } from '@mantine/core';

function Demo() {
  function BoxComponent({ children, ...props }: { children: ReactNode; [key: string]: any }) {
    return (
      <Box {...props} p="md" w="200px" c="white" style={{ borderRadius: '8px' }}>
        {children}
      </Box>
    );
  }

  return (
    <Marquee gap={{ base: 'xs', md: 'xl' }} fadeEdges="linear" w="100%">
      <BoxComponent bg="blue">Hello World #1</BoxComponent>
      <BoxComponent bg="cyan">Hope you like it #2</BoxComponent>
      <BoxComponent bg="indigo">Have a nice day #3</BoxComponent>
      <BoxComponent bg="teal">Goodbye #4</BoxComponent>
    </Marquee>
  );
}

Drop-in for Mantine core Marquee

This component is a superset of the built-in @mantine/core Marquee: existing snippets keep working after changing the import path. The props, the gap values, the Styles API selectors (root, content, group), attributes and the root data attributes carry over. A few things to know:

  • orientation="horizontal" | "vertical" is accepted as an alias of vertical, and fadeEdgeSize as an alias of fadeEdgesSize (singular spelling).
  • duration is expressed in seconds here, while @mantine/core measures it in milliseconds: core duration={100000} corresponds to duration={100}. The default is 20 s here and 100 s in core: set duration={100} to keep core's pace.
  • fadeEdges is off by default here and on by default in core: add fadeEdges to keep the fade after switching. Its default size is "xs" (11%) here and 5% in core: set fadeEdgeSize="5%" to match.
  • fadeEdgeColor resolves theme colors: "red" is the theme's red here, while core hands the string to CSS as the named color. Any other CSS color means the same in both.

On top of that you get responsive gap and vertical, fade-edge shapes (linear, ellipse, rect) as a mask or in a color, per-axis fade sizes for rect, a pause on keyboard focus, and the isometric and circle 3D variants.

Example: Testimonials

Your customers feedback is important to you. Use Marquee to display your testimonials.

John Doe

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Jane Doe

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Jessica Doe

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Jack Doe

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Jill Doe

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

John Doe

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Jane Doe

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Jessica Doe

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Jack Doe

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

Jill Doe

Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua.

import { Marquee } from '@gfazioli/mantine-marquee';
import { Avatar, Card, Group, Rating, Stack, Text, Title } from '@mantine/core';
import { ReactNode } from 'react';
import { testimonials } from './testimonials';

function Demo() {
  function BoxComponent({
    children,
    avatar,
    name,
    rating,
    ...props
  }: {
    children: ReactNode;
    avatar: string;
    name: string;
    [key: string]: any;
  }) {
    return (
      <Card w={200} shadow="sm" padding="lg" radius="md" withBorder>
        <Card.Section p="md">
          <Group>
            <Avatar size="md" radius="xl" src={avatar} />
            <Title order={4}>{name}</Title>
          </Group>
        </Card.Section>
        <Stack>
          <Text size="sm" c="dimmed">
            {children}
          </Text>
          <Group justify="end">
            <Rating value={rating} />
          </Group>
        </Stack>
      </Card>
    );
  }

  return (
    <Marquee pauseOnHover fadeEdges>
      {testimonials.map(({ text, ...testimonial }, index) => (
        <BoxComponent key={index} {...testimonial}>
          {text}
        </BoxComponent>
      ))}
    </Marquee>
  );
}

Example: Branding

One of the most common use cases for Marquee is to create branding banners.

import { ReactNode } from 'react';
import { Marquee } from '@gfazioli/mantine-marquee';
import { ThemeIcon } from '@mantine/core';
import {
  IconBrand4chan,
  IconBrandAmazon,
  IconBrandBing,
  IconBrandGithub,
  IconBrandMantine,
  IconBrandWhatsapp,
  IconBrandWordpress,
} from "@tabler/icons-react";

function Demo() {
  const iconsBrand = [
    <IconBrand4chan style={{ width: '70%', height: '70%' }} />,
    <IconBrandWhatsapp style={{ width: '70%', height: '70%' }} />,
    <IconBrandWordpress style={{ width: '70%', height: '70%' }} />,
    <IconBrandBing style={{ width: '70%', height: '70%' }} />,
    <IconBrandGithub style={{ width: '70%', height: '70%' }} />,
    <IconBrandMantine style={{ width: '70%', height: '70%' }} />,
    <IconBrandAmazon style={{ width: '70%', height: '70%' }} />,
  ];

  function BoxComponent({ children, ...props }: { children: ReactNode; [key: string]: any }) {
    return (
      <ThemeIcon {...props} variant="transparent" size="120px">
        {children}
      </ThemeIcon>
    );
  }

  return (
    <Marquee pauseOnHover fadeEdges>
      {iconsBrand.map((icon, index) => (
        <BoxComponent key={index}>{icon}</BoxComponent>
      ))}
    </Marquee>
  );
}

Example: Isometric tech stack

The isometric variant turns a plain logo strip into a 3D "tech stack on a desk" hero — perfect for landing pages, integrations sections, or a tools showcase.

React

TypeScript

Next.js

Mantine

Vercel

Tailwind

Figma

GitHub

React

TypeScript

Next.js

Mantine

Vercel

Tailwind

Figma

GitHub

import { Marquee } from '@gfazioli/mantine-marquee';
import { Group, Paper, Text, ThemeIcon } from '@mantine/core';
import {
  IconBrandFigma,
  IconBrandGithub,
  IconBrandMantine,
  IconBrandNextjs,
  IconBrandReact,
  IconBrandTailwind,
  IconBrandTypescript,
  IconBrandVercel,
} from '@tabler/icons-react';

const stack = [
  { label: 'React', icon: <IconBrandReact />, color: 'cyan' },
  { label: 'TypeScript', icon: <IconBrandTypescript />, color: 'blue' },
  { label: 'Next.js', icon: <IconBrandNextjs />, color: 'dark' },
  { label: 'Mantine', icon: <IconBrandMantine />, color: 'indigo' },
  { label: 'Vercel', icon: <IconBrandVercel />, color: 'dark' },
  { label: 'Tailwind', icon: <IconBrandTailwind />, color: 'teal' },
  { label: 'Figma', icon: <IconBrandFigma />, color: 'grape' },
  { label: 'GitHub', icon: <IconBrandGithub />, color: 'gray' },
];

function Demo() {
  return (
    <Marquee
      variant="isometric"
      tilt={50}
      rotate={30}
      skew={10}
      perspective={650}
      duration={16}
      fadeEdges="ellipse"
      pauseOnHover
      h={340}
    >
      {stack.map((tech) => (
        <Paper key={tech.label} withBorder radius="md" p="md" w={210} shadow="md">
          <Group gap="sm" wrap="nowrap">
            <ThemeIcon variant="light" color={tech.color} size={42} radius="md">
              {tech.icon}
            </ThemeIcon>
            <Text fw={600}>{tech.label}</Text>
          </Group>
        </Paper>
      ))}
    </Marquee>
  );
}

Example: Circle avatar ring

The circle variant arranges items around a rotating 3D ring — a carousel for team avatars, community members, or a brand showcase. A slightly negative tilt views the ring from just below; hover to pause and click an avatar.

import { Marquee } from '@gfazioli/mantine-marquee';
import { Avatar, Flex } from '@mantine/core';

const base = 'https://raw.githubusercontent.com/mantinedev/mantine/master/.demo/avatars';
const avatars = [
  `${base}/avatar-1.png`,
  `${base}/avatar-2.png`,
  `${base}/avatar-3.png`,
  `${base}/avatar-4.png`,
  `${base}/avatar-5.png`,
  `${base}/avatar-6.png`,
  `${base}/avatar-7.png`,
  `${base}/avatar-8.png`,
];

function Demo() {
  return (
    <Flex w="100%" h={360} align="center" justify="center">
      <Marquee
        variant="circle"
        radius={240}
        tilt={-14}
        perspective={900}
        duration={22}
        pauseOnHover
        w="100%"
        h={360}
      >
        {avatars.map((src, index) => (
          <Avatar
            key={index}
            src={src}
            size={96}
            radius="xl"
            style={{
              border: '3px solid var(--mantine-color-body)',
              boxShadow: 'var(--mantine-shadow-lg)',
            }}
          />
        ))}
      </Marquee>
    </Flex>
  );
}