Prev & Next Buttons

This guide shows you how to add previous and next buttons to control carousel navigation in Embla Carousel.


Prerequisites

Button placement

If your carousel is draggable, the root node — the element passed to the EmblaCarousel initializer (e.g., .embla__viewport) — responds to pointer events. To avoid unintended drag interactions when users click them, place your navigation buttons outside the root element, like this:

import EmblaCarousel from 'embla-carousel'
const wrapperNode = document.querySelector('.embla')const viewportNode = wrapperNode.querySelector('.embla__viewport')const emblaApi = EmblaCarousel(viewportNode, { loop: false })
<div class="embla">  <div class="embla__viewport">    <div class="embla__container">      <div class="embla__slide">Slide 1</div>      <div class="embla__slide">Slide 2</div>      <div class="embla__slide">Slide 3</div>    </div>  </div>
  <!-- Place your navigation buttons here --></div>

Adding buttons

Adding navigation buttons is simple. Create your button elements and connect them to Embla's navigation methods — goToPrev and goToNext.

These methods let you scroll the carousel programmatically when users click the buttons, giving them an intuitive way to move between slides.

import EmblaCarousel from 'embla-carousel'
const wrapperNode = document.querySelector('.embla')const viewportNode = wrapperNode.querySelector('.embla__viewport')const prevButtonNode = wrapperNode.querySelector('.embla__prev')const nextButtonNode = wrapperNode.querySelector('.embla__next')
const emblaApi = EmblaCarousel(viewportNode, { loop: false })
prevButtonNode.addEventListener('click', () => emblaApi.scrollToPrev(), false)nextButtonNode.addEventListener('click', () => emblaApi.scrollToNext(), false)
<div class="embla">  <div class="embla__viewport">    <div class="embla__container">      <div class="embla__slide">Slide 1</div>      <div class="embla__slide">Slide 2</div>      <div class="embla__slide">Slide 3</div>    </div>  </div>
  <button class="embla__prev">Scroll to prev</button>  <button class="embla__next">Scroll to next</button></div>

Buttons state

We'll enhance the user experience by disabling the navigation buttons when the carousel can't scroll any further.

For non-looping carousels, it's good UX to indicate when the navigation buttons can't be used — for example, when you're already at the first or last slide.

We'll handle this by toggling each button's state between enabled and disabled, updating them when:

  • The select event fires — when the selected scroll snap changes.
  • The reinit event fires — when the carousel resets.
.embla__prev:disabled,.embla__next:disabled {  opacity: 0.3;  cursor: not-allowed;}
import EmblaCarousel from 'embla-carousel'
const wrapperNode = document.querySelector('.embla')const viewportNode = wrapperNode.querySelector('.embla__viewport')const prevButtonNode = wrapperNode.querySelector('.embla__prev')const nextButtonNode = wrapperNode.querySelector('.embla__next')
const emblaApi = EmblaCarousel(viewportNode, { loop: false })
prevButtonNode.addEventListener('click', () => emblaApi.scrollToPrev(), false)nextButtonNode.addEventListener('click', () => emblaApi.scrollToNext(), false)
const toggleButtonsDisabled = (emblaApi) => {  const setButtonState = (button, enabled) => {    button.toggleAttribute('disabled', !enabled)  }  setButtonState(prevButtonNode, emblaApi.canScrollToPrev())  setButtonState(nextButtonNode, emblaApi.canScrollToNext())}
toggleButtonsDisabled(emblaApi)emblaApi.on('select', toggleButtonsDisabled)emblaApi.on('reinit', toggleButtonsDisabled)
<div class="embla">  <div class="embla__viewport">    <div class="embla__container">      <div class="embla__slide">Slide 1</div>      <div class="embla__slide">Slide 2</div>      <div class="embla__slide">Slide 3</div>    </div>  </div>
  <button class="embla__prev">Scroll to prev</button>  <button class="embla__next">Scroll to next</button></div>

Notes

  • Button placement: Place the prev/next buttons outside the Embla root element (the one passed to the initializer, e.g. .embla__viewport) to prevent accidental drag interactions.
  • Looping carousels: In looping carousels with enough slides to wrap around, the prev/next methods always trigger scrolling — buttons are never disabled.
  • No-op behavior: On non-looping carousels, calling goToPrev or goToNext when already at the first or last slide has no effect. This is normal and ensures consistent, predictable behavior.
Edit this page on GitHub