Skip to main content
Default Gray Amethyst

Carousel

<mo-carousel> | MOCarousel
Since 3.0 stable

Carousels display an arbitrary number of content slides along a horizontal or vertical axis.

A snowy winter day at a quarry. Sun shines on the mining machine in a quarry. A busy city viewed from above. A large quarry for mining salt. A conveyor belt moving rocks in Lehigh Hanson.
<mo-carousel pagination navigation mouse-dragging loop>
  <mo-carousel-item>
    <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
  </mo-carousel-item>
</mo-carousel>
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';

const App = () => (
  <>
    <MOCarousel pagination navigation mouse-dragging loop>
      <MOCarouselItem>
        <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
      </MOCarouselItem>
    </MOCarousel>
  </>
);

Examples

Pagination

Use the pagination attribute to show the total number of slides and the current slide as a set of interactive dots.

A snowy winter day at a quarry. Sun shines on the mining machine in a quarry. A busy city viewed from above. A large quarry for mining salt. A conveyor belt moving rocks in Lehigh Hanson.
<mo-carousel pagination>
  <mo-carousel-item>
    <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
  </mo-carousel-item>
</mo-carousel>
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';

const App = () => (
  <MOCarousel pagination>
    <MOCarouselItem>
      <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
    </MOCarouselItem>
  </MOCarousel>
);

Use the navigation attribute to show previous and next buttons.

A snowy winter day at a quarry. Sun shines on the mining machine in a quarry. A busy city viewed from above. A large quarry for mining salt. A conveyor belt moving rocks in Lehigh Hanson.
<mo-carousel navigation>
  <mo-carousel-item>
    <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
  </mo-carousel-item>
</mo-carousel>
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';

const App = () => (
  <MOCarousel navigation>
    <MOCarouselItem>
      <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
    </MOCarouselItem>
  </MOCarousel>
);

Looping

By default, the carousel will not advanced beyond the first and last slides. You can change this behavior and force the carousel to “wrap” with the loop attribute.

A snowy winter day at a quarry. Sun shines on the mining machine in a quarry. A busy city viewed from above. A large quarry for mining salt. A conveyor belt moving rocks in Lehigh Hanson.
<mo-carousel loop navigation pagination>
  <mo-carousel-item>
    <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
  </mo-carousel-item>
</mo-carousel>
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';

const App = () => (
  <MOCarousel loop navigation pagination>
    <MOCarouselItem>
      <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
    </MOCarouselItem>
  </MOCarousel>
);

Autoplay

The carousel will automatically advance when the autoplay attribute is used. To change how long a slide is shown before advancing, set autoplay-interval to the desired number of milliseconds. For best results, use the loop attribute when autoplay is enabled. Note that autoplay will pause while the user interacts with the carousel.

A snowy winter day at a quarry. Sun shines on the mining machine in a quarry. A busy city viewed from above. A large quarry for mining salt. A conveyor belt moving rocks in Lehigh Hanson.
<mo-carousel autoplay loop pagination>
  <mo-carousel-item>
    <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
  </mo-carousel-item>
</mo-carousel>
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';

const App = () => (
  <MOCarousel autoplay loop pagination>
    <MOCarouselItem>
      <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
    </MOCarouselItem>
    <MOCarouselItem>
      <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
    </MOCarouselItem>
  </MOCarousel>
);

Mouse dragging

The carousel uses scroll snap to position slides at various snap positions. This allows users to scroll through the slides very naturally, especially on touch devices. Unfortunately, desktop users won’t be able to click and drag with a mouse, which can feel unnatural. Adding the mouse-dragging attribute can help with this.

This example is best demonstrated using a mouse. Try clicking and dragging the slide to move it. Then toggle the switch and try again.

A snowy winter day at a quarry. Sun shines on the mining machine in a quarry. A busy city viewed from above. A large quarry for mining salt. A conveyor belt moving rocks in Lehigh Hanson. Enable mouse dragging
<div class="mouse-dragging">
  <mo-carousel pagination>
    <mo-carousel-item>
      <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
    </mo-carousel-item>
    <mo-carousel-item>
      <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
    </mo-carousel-item>
    <mo-carousel-item>
      <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
    </mo-carousel-item>
    <mo-carousel-item>
      <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
    </mo-carousel-item>
    <mo-carousel-item>
      <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
    </mo-carousel-item>
  </mo-carousel>

  <mo-divider></mo-divider>

  <mo-switch>
    <mo-icon name="ok" slot="suffix"></mo-icon>
    Enable mouse dragging
  </mo-switch>
</div>

<script>
  const container = document.querySelector('.mouse-dragging');
  const carousel = container.querySelector('mo-carousel');
  const toggle = container.querySelector('mo-switch');

  toggle.addEventListener('mo-change', () => {
    carousel.toggleAttribute('mouse-dragging', toggle.checked);
  });
</script>
import { useState } from 'react';
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';
import MODivider from '@metsooutotec/modes-web-components/dist/react/divider';
import MOSwitch from '@metsooutotec/modes-web-components/dist/react/switch';

const App = () => {
  const [isEnabled, setIsEnabled] = useState(false);

  return (
    <>
      <MOCarousel navigation mouseDragging={isEnabled}>
        <MOCarouselItem>
          <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
        </MOCarouselItem>
        <MOCarouselItem>
          <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
        </MOCarouselItem>
        <MOCarouselItem>
          <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
        </MOCarouselItem>
        <MOCarouselItem>
          <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
        </MOCarouselItem>
        <MOCarouselItem>
          <img
            alt="A conveyor belt moving rocks in Lehigh Hanson."
            src="/assets/examples/carousel/lehigh-hanson.webp"
          />
        </MOCarouselItem>
      </MOCarousel>

      <MODivider></MODivider>

      <MOSwitch checked={isEnabled} onMOInput={() => setIsEnabled(!isEnabled)}>
        <MOIcon name="ok" slot="suffix"></MOIcon>
        Enable mouse dragging
      </MOSwitch>
    </>
  );
};

Multiple slides per view

The slides-per-page attribute makes it possible to display multiple slides at a time. You can also use the slides-per-move attribute to advance more than once slide at a time, if desired.

Slide 1 Slide 2 Slide 3 Slide 4 Slide 5 Slide 6
<mo-carousel navigation pagination slides-per-page="2" slides-per-move="2">
  <mo-carousel-item style="background: var(--mo-color-highlight-1-lighter);">Slide 1</mo-carousel-item>
  <mo-carousel-item style="background: var(--mo-color-highlight-2-lighter);">Slide 2</mo-carousel-item>
  <mo-carousel-item style="background: var(--mo-color-highlight-3-lighter)">Slide 3</mo-carousel-item>
  <mo-carousel-item style="background: var(--mo-color-highlight-4-lighter);">Slide 4</mo-carousel-item>
  <mo-carousel-item style="background: var(--mo-color-highlight-5-lighter);">Slide 5</mo-carousel-item>
  <mo-carousel-item style="background: var(--mo-color-highlight-6-lighter);">Slide 6</mo-carousel-item>
</mo-carousel>
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';

const App = () => (
  <MOCarousel navigation pagination slidesPerPage={2} slidesPerMove={2}>
    <MOCarouselItem style={{ background: 'var(--mo-color-highlight-1-lighter)' }}>Slide 1</MOCarouselItem>
    <MOCarouselItem style={{ background: 'var(--mo-color-highlight-2-lighter)' }}>Slide 2</MOCarouselItem>
    <MOCarouselItem style={{ background: 'var(--mo-color-highlight-3-lighter)' }}>Slide 3</MOCarouselItem>
    <MOCarouselItem style={{ background: 'var(--mo-color-highlight-4-lighter)' }}>Slide 4</MOCarouselItem>
    <MOCarouselItem style={{ background: 'var(--mo-color-highlight-5-lighter)' }}>Slide 5</MOCarouselItem>
    <MOCarouselItem style={{ background: 'var(--mo-color-highlight-6-lighter)' }}>Slide 6</MOCarouselItem>
  </MOCarousel>
);

Adding and removing slides

The content of the carousel can be changed by adding or removing carousel items. The carousel will update itself automatically.

Slide 1 Slide 2 Slide 3
<mo-carousel class="dynamic-carousel" pagination navigation>
  <mo-carousel-item style="background: var(--mo-color-highlight-1-lighter)">Slide 1</mo-carousel-item>
  <mo-carousel-item style="background: var(--mo-color-highlight-2-lighter)">Slide 2</mo-carousel-item>
  <mo-carousel-item style="background: var(--mo-color-highlight-3-lighter)">Slide 3</mo-carousel-item>
</mo-carousel>

<div class="carousel-options">
  <mo-button id="dynamic-add">Add slide</mo-button>
  <mo-button id="dynamic-remove">Remove slide</mo-button>
</div>

<style>
  .dynamic-carousel {
    --aspect-ratio: 3 / 2;
  }

  .dynamic-carousel ~ .carousel-options {
    display: flex;
    justify-content: center;
    gap: var(--mo-spacing-x-small);
    margin-top: var(--mo-spacing-large);
  }

  .dynamic-carousel mo-carousel-item {
    flex: 0 0 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    font-size: var(--mo-font-size-2x-large);
  }
</style>

<script>
  (() => {
    const dynamicCarousel = document.querySelector('.dynamic-carousel');
    const dynamicAdd = document.querySelector('#dynamic-add');
    const dynamicRemove = document.querySelector('#dynamic-remove');
    const colors = ['1', '2', '3', '4', '5', '6'];
    let colorIndex = 2;

    const addSlide = () => {
      const slide = document.createElement('mo-carousel-item');
      const color = colors[++colorIndex % colors.length];
      slide.innerText = `Slide ${dynamicCarousel.children.length + 1}`;
      slide.style.setProperty('background', `var(--mo-color-highlight-${color}-lighter)`);
      dynamicCarousel.appendChild(slide);
      dynamicRemove.disabled = false;
    };

    const removeSlide = () => {
      const slide = dynamicCarousel.children[dynamicCarousel.children.length - 1];
      const numSlides = dynamicCarousel.querySelectorAll('mo-carousel-item').length;

      if (numSlides > 1) {
        slide.remove();
        colorIndex--;
      }

      dynamicRemove.disabled = numSlides - 1 <= 1;
    };

    dynamicAdd.addEventListener('click', addSlide);
    dynamicRemove.addEventListener('click', removeSlide);
  })();
</script>
import { useState } from 'react';
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';

const css = `
  .dynamic-carousel {
    --aspect-ratio: 3 / 2;
  }

  .dynamic-carousel ~ .carousel-options {
    display: flex;
    justify-content: center;
    margin-top: var(--mo-spacing-large);
  }

  .dynamic-carousel mo-carousel-item {
    flex: 0 0 100%;
    display: flex;
    align-items: center;
    justify-content: center;
    color: white;
    font-size: var(--mo-font-size-2x-large);
  }
`;

const App = () => {
  const [slides, setSlides] = useState(['#204ed8', '#be133d', '#6e28d9']);
  const colors = ['red', 'orange', 'yellow', 'green', 'blue', 'violet'];

  const addSlide = () => {
    setSlides([...slides, getRandomColor()]);
  };

  const removeSlide = () => {
    setSlides(slides.slice(0, -1));
  };

  return (
    <>
      <MOCarousel className="dynamic-carousel" pagination navigation>
        {slides.map((color, i) => (
          <MOCarouselItem style={{ background: colors[i % colors.length }}>
            Slide {i}
          </MOCarouselItem>
        ))}
      </MOCarousel>

      <div className="carousel-options">
        <MOButton onClick={addSlide}>Add slide</MOButton>
        <MOButton onClick={removeSlide}>Remove slide</MOButton>
      </div>

      <style>{css}</style>
    </>
  );
};

Vertical scrolling

Setting the orientation attribute to vertical will render the carousel in a vertical layout. If the content of your slides vary in height, you will need to set amn explicit height or max-height on the carousel using CSS.

A snowy winter day at a quarry. Sun shines on the mining machine in a quarry. A busy city viewed from above. A large quarry for mining salt. A conveyor belt moving rocks in Lehigh Hanson.
<mo-carousel class="vertical" pagination orientation="vertical">
  <mo-carousel-item>
    <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
  </mo-carousel-item>
</mo-carousel>
<style>
  .vertical {
    max-height: 400px;
  }

  .vertical::part(base) {
    grid-template-areas: 'slides slides pagination';
  }

  .vertical::part(pagination) {
    flex-direction: column;
  }

  .vertical::part(navigation) {
    transform: rotate(90deg);
    display: flex;
  }
</style>
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';

const css = `
  .vertical {
    max-height: 400px;
  }

  .vertical::part(base) {
    grid-template-areas: 'slides slides pagination';
  }

  .vertical::part(pagination) {
    flex-direction: column;
  }

  .vertical::part(navigation) {
    transform: rotate(90deg);
    display: flex;
  }
`;

const App = () => (
  <>
    <MOCarousel className="vertical" loop pagination orientation="vertical">
      <MOCarouselItem>
        <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
      </MOCarouselItem>
    </MOCarousel>
    <style>{css}</style>
  </>
);

Aspect ratio

Use the --aspect-ratio custom property to customize the size of the carousel’s viewport from the default value of 16/9.

A snowy winter day at a quarry. Sun shines on the mining machine in a quarry. A busy city viewed from above. A large quarry for mining salt. A conveyor belt moving rocks in Lehigh Hanson. 1/1 3/2 16/9
<mo-carousel class="aspect-ratio" navigation pagination style="--aspect-ratio: 3/2;">
  <mo-carousel-item>
    <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
  </mo-carousel-item>
</mo-carousel>

<mo-divider></mo-divider>

<mo-select label="Aspect ratio" name="aspect" value="3/2">
  <mo-option value="1/1">1/1</mo-option>
  <mo-option value="3/2">3/2</mo-option>
  <mo-option value="16/9">16/9</mo-option>
</mo-select>

<script>
  (() => {
    const carousel = document.querySelector('mo-carousel.aspect-ratio');
    const aspectRatio = document.querySelector('mo-select[name="aspect"]');

    aspectRatio.addEventListener('mo-change', () => {
      carousel.style.setProperty('--aspect-ratio', aspectRatio.value);
    });
  })();
</script>
import { useState } from 'react';
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';
import MODivider from '@metsooutotec/modes-web-components/dist/react/divider';
import MOSelect from '@metsooutotec/modes-web-components/dist/react/select';
import MOOption from '@metsooutotec/modes-web-components/dist/react/option';

const App = () => {
  const [aspectRatio, setAspectRatio] = useState('3/2');

  return (
    <>
      <MOCarousel className="aspect-ratio" navigation pagination style={{ '--aspect-ratio': aspectRatio }}>
        <MOCarouselItem>
          <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
        </MOCarouselItem>
        <MOCarouselItem>
          <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
        </MOCarouselItem>
        <MOCarouselItem>
          <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
        </MOCarouselItem>
        <MOCarouselItem>
          <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
        </MOCarouselItem>
        <MOCarouselItem>
          <img
            alt="A conveyor belt moving rocks in Lehigh Hanson."
            src="/assets/examples/carousel/lehigh-hanson.webp"
          />
        </MOCarouselItem>
      </MOCarousel>

      <MODivider />

      <MOSelect
        label="Aspect ratio"
        name="aspect"
        value={aspectRatio}
        onMOChange={event => setAspectRatio(event.target.value)}
      >
        <MOOption value="1 / 1">1 / 1</MOOption>
        <MOOption value="3 / 2">3 / 2</MOOption>
        <MOOption value="16 / 9">16 / 9</MOOption>
      </MOSelect>

      <style>{css}</style>
    </>
  );
};

Scroll hint

Use the --scroll-hint custom property to add inline padding in horizontal carousels and block padding in vertical carousels. This will make the closest slides slightly visible, hinting that there are more items in the carousel.

A snowy winter day at a quarry. Sun shines on the mining machine in a quarry. A busy city viewed from above. A large quarry for mining salt. A conveyor belt moving rocks in Lehigh Hanson.
<mo-carousel class="scroll-hint" pagination style="--scroll-hint: 10%;">
  <mo-carousel-item>
    <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
  </mo-carousel-item>
</mo-carousel>
import { useState } from 'react';
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';
import MODivider from '@metsooutotec/modes-web-components/dist/react/divider';
import MORange from '@metsooutotec/modes-web-components/dist/react/range';

const App = () => (
  <>
    <MOCarousel className="scroll-hint" pagination style={{ '--scroll-hint': '10%' }}>
      <MOCarouselItem>
        <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
      </MOCarouselItem>
      <MOCarouselItem>
        <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
      </MOCarouselItem>
    </MOCarousel>
  </>
);

The carousel has a robust API that makes it possible to extend and customize. This example syncs the active slide with a set of thumbnails, effectively creating a gallery-style carousel.

A snowy winter day at a quarry. Sun shines on the mining machine in a quarry. A busy city viewed from above. A large quarry for mining salt. A conveyor belt moving rocks in Lehigh Hanson.
Thumbnail by 1 Thumbnail by 2 Thumbnail by 3 Thumbnail by 4 Thumbnail by 5
<mo-carousel class="carousel-thumbnails" navigation loop>
  <mo-carousel-item>
    <img alt="A snowy winter day at a quarry." src="/assets/examples/carousel/winter-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="Sun shines on the mining machine in a quarry." src="/assets/examples/carousel/sun-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A busy city viewed from above." src="/assets/examples/carousel/busy-city.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A large quarry for mining salt." src="/assets/examples/carousel/salt-quarry.webp" />
  </mo-carousel-item>
  <mo-carousel-item>
    <img alt="A conveyor belt moving rocks in Lehigh Hanson." src="/assets/examples/carousel/lehigh-hanson.webp" />
  </mo-carousel-item>
</mo-carousel>

<div class="thumbnails">
  <div class="thumbnails__scroller">
    <img alt="Thumbnail by 1" class="thumbnails__image active" src="/assets/examples/carousel/winter-quarry.webp" />
    <img alt="Thumbnail by 2" class="thumbnails__image" src="/assets/examples/carousel/sun-quarry.webp" />
    <img alt="Thumbnail by 3" class="thumbnails__image" src="/assets/examples/carousel/busy-city.webp" />
    <img alt="Thumbnail by 4" class="thumbnails__image" src="/assets/examples/carousel/salt-quarry.webp" />
    <img alt="Thumbnail by 5" class="thumbnails__image" src="/assets/examples/carousel/lehigh-hanson.webp" />
  </div>
</div>

<style>
  .carousel-thumbnails {
    --slide-aspect-ratio: 3 / 2;
  }

  .thumbnails {
    display: flex;
    justify-content: center;
  }

  .thumbnails__scroller {
    display: flex;
    gap: var(--mo-spacing-small);
    overflow-x: auto;
    scrollbar-width: none;
    scroll-behavior: smooth;
    scroll-padding: var(--mo-spacing-small);
  }

  .thumbnails__scroller::-webkit-scrollbar {
    display: none;
  }

  .thumbnails__image {
    width: 64px;
    height: 64px;
    object-fit: cover;

    opacity: 0.3;
    will-change: opacity;
    transition: 250ms opacity;

    cursor: pointer;
  }

  .thumbnails__image.active {
    opacity: 1;
  }
</style>

<script>
  {
    const carousel = document.querySelector('.carousel-thumbnails');
    const scroller = document.querySelector('.thumbnails__scroller');
    const thumbnails = document.querySelectorAll('.thumbnails__image');

    scroller.addEventListener('click', e => {
      const target = e.target;

      if (target.matches('.thumbnails__image')) {
        const index = [...thumbnails].indexOf(target);
        carousel.goToSlide(index);
      }
    });

    carousel.addEventListener('mo-slide-change', e => {
      const slideIndex = e.detail.index;

      [...thumbnails].forEach((thumb, i) => {
        thumb.classList.toggle('active', i === slideIndex);
        if (i === slideIndex) {
          thumb.scrollIntoView({
            block: 'nearest'
          });
        }
      });
    });
  }
</script>
import { useRef } from 'react';
import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel';
import MOCarouselItem from '@metsooutotec/modes-web-components/dist/react/carousel-item';
import MODivider from '@metsooutotec/modes-web-components/dist/react/divider';
import MORange from '@metsooutotec/modes-web-components/dist/react/range';

const css = `
  .carousel-thumbnails {
    --slide-aspect-ratio: 3 / 2;
  }

  .thumbnails {
    display: flex;
    justify-content: center;
  }

  .thumbnails__scroller {
    display: flex;
    gap: var(--mo-spacing-small);
    overflow-x: auto;
    scrollbar-width: none;
    scroll-behavior: smooth;
    scroll-padding: var(--mo-spacing-small);
  }

  .thumbnails__scroller::-webkit-scrollbar {
    display: none;
  }

  .thumbnails__image {
    width: 64px;
    height: 64px;
    object-fit: cover;

    opacity: 0.3;
    will-change: opacity;
    transition: 250ms opacity;

    cursor: pointer;
  }

  .thumbnails__image.active {
    opacity: 1;
  }
`;

const images = [
  {
    src: '/assets/examples/carousel/winter-quarry.webp',
    alt: 'The sun shines on the mountains and trees (by Adam Kool on Unsplash'
  },
  {
    src: '/assets/examples/carousel/sun-quarry.webp',
    alt: 'A waterfall in the middle of a forest (by Thomas Kelly on Unsplash'
  },
  {
    src: '/assets/examples/carousel/busy-city.webp',
    alt: 'The sun is setting over a lavender field (by Leonard Cotte on Unsplash'
  },
  {
    src: '/assets/examples/carousel/salt-quarry.webp',
    alt: 'A field of grass with the sun setting in the background (by Sapan Patel on Unsplash'
  },
  {
    src: '/assets/examples/carousel/lehigh-hanson.webp',
    alt: 'A scenic view of a mountain with clouds rolling in (by V2osk on Unsplash'
  }
];

const App = () => {
  const carouselRef = useRef();
  const thumbnailsRef = useRef();
  const [currentSlide, setCurrentSlide] = useState(0);

  useEffect(() => {
    const thumbnails = Array.from(thumbnailsRef.current.querySelectorAll('.thumbnails__image'));

    thumbnails[currentSlide]..scrollIntoView({
      block: 'nearest'
    });
  }, [currentSlide]);

  const handleThumbnailClick = (index) => {
    carouselRef.current.goToSlide(index);
  }

  const handleSlideChange = (event) => {
    const slideIndex = e.detail.index;
    setCurrentSlide(slideIndex);
  }

  return (
    <>
      <MOCarousel className="carousel-thumbnails" navigation loop onMOSlideChange={handleSlideChange}>
        {images.map({ src, alt }) => (
          <MOCarouselItem>
            <img
              alt={alt}
              src={src}
            />
          </MOCarouselItem>
        )}
      </MOCarousel>

      <div class="thumbnails">
        <div class="thumbnails__scroller">
          {images.map({ src, alt }, i) => (
            <img
              alt={`Thumbnail by ${i + 1}`}
              className={`thumbnails__image ${i === currentSlide ? 'active' : ''}`}
              onClick={() => handleThumbnailClick(i)}
              src={src}
            />
          )}
        </div>
      </div>
      <style>{css}</style>
    </>
  );
};

Importing

If you’re using the autoloader or the traditional loader, you can ignore this section. Otherwise, feel free to use any of the following snippets to cherry pick this component.

Bundler React Script

To import this component using a bundler:

import '@metsooutotec/modes-web-components/dist/components/carousel/carousel.js';

To import this component as a React component:

import MOCarousel from '@metsooutotec/modes-web-components/dist/react/carousel/';

To import this component using a script tag:

<script type="module" src="https://modes-web.metso.com/dist/components/cdn/components/carousel/carousel.js"></script>

Slots

Name Description
(default) The carousel’s main content, one or more <mo-carousel-item> elements.
next-icon Optional next icon to use instead of the default. Works best with <mo-icon>.
previous-icon Optional previous icon to use instead of the default. Works best with <mo-icon>.

Learn more about using slots.

Properties

Name Description Reflects Type Default
loop When set, allows the user to navigate the carousel in the same direction indefinitely. boolean false
navigation When set, show the carousel’s navigation. boolean false
pagination When set, show the carousel’s pagination indicators. boolean false
autoplay When set, the slides will scroll automatically when the user is not interacting with them. boolean false
autoplayInterval
autoplay-interval
Specifies the amount of time, in milliseconds, between each automatic scroll. number 3000
slidesPerPage
slides-per-page
Specifies how many slides should be shown at a given time. number 1
slidesPerMove
slides-per-move
Specifies the number of slides the carousel will advance when scrolling, useful when specifying a slides-per-page greater than one. It can’t be higher than slides-per-page. number 1
orientation Specifies the orientation in which the carousel will lay out. 'horizontal' | 'vertical' 'horizontal'
mouseDragging
mouse-dragging
When set, it is possible to scroll through the slides by dragging them with the mouse. boolean false
updateComplete A read-only promise that resolves when the component has finished updating.

Learn more about attributes and properties.

Events

Name React Event Description Event Detail
mo-slide-change onMoSlideChange Emitted when the active slide changes. { index: number, slide: MOCarouselItem }

Learn more about events.

Methods

Name Description Arguments
previous() Move the carousel backward by slides-per-move slides. behavior: ScrollBehavior
next() Move the carousel forward by slides-per-move slides. behavior: ScrollBehavior
goToSlide() Scrolls the carousel to the slide specified by index. index: number, behavior: ScrollBehavior

Learn more about methods.

Custom Properties

Name Description Default
--slide-gap The space between each slide.
--aspect-ratio The aspect ratio of each slide. 16/9
--scroll-hint The amount of padding to apply to the scroll area, allowing adjacent slides to become partially visible as a scroll hint.

Learn more about customizing CSS custom properties.

Parts

Name Description
base The carousel’s internal wrapper.
scroll-container The scroll container that wraps the slides.
pagination The pagination indicators wrapper.
pagination-item The pagination indicator.
pagination-item--active Applied when the item is active.
navigation The navigation wrapper.
navigation-button The navigation button.
navigation-button--previous Applied to the previous button.
navigation-button--next Applied to the next button.

Learn more about customizing CSS parts.

Dependencies

This component automatically imports the following dependencies.