Events
Embla Carousel provides a set of events you can listen to for reacting to carousel updates and user interactions.
Usage
Events are available only on an initialized carousel instance. They fire throughout the carousel's lifecycle, and any added listeners remain active even after a hard reset with the reInit method.
Adding event listeners
After initializing the carousel, you can subscribe to events. The following example shows how to listen for the slidesInView event:
import EmblaCarousel from 'embla-carousel'
const wrapperNode = document.querySelector('.embla')const viewportNode = wrapperNode.querySelector('.embla__viewport')
const emblaApi = EmblaCarousel(viewportNode, { loop: true })
const logSlidesInView = (emblaApi) => { console.log(emblaApi.slidesInView())}
emblaApi.on('slidesInView', logSlidesInView)<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></div>import React, { useEffect } from 'react'import useEmblaCarousel from 'embla-carousel-react'
export function EmblaCarousel() { const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true })
const logSlidesInView = (emblaApi) => { console.log(emblaApi.slidesInView()) }
useEffect(() => { if (!emblaApi) return emblaApi.on('slidesInView', logSlidesInView) }, [emblaApi])
return ( <div className="embla"> <div className="embla__viewport" ref={emblaRef}> <div className="embla__container"> <div className="embla__slide">Slide 1</div> <div className="embla__slide">Slide 2</div> <div className="embla__slide">Slide 3</div> </div> </div> </div> )}<script setup>import { watch } from 'vue'import useEmblaCarousel from 'embla-carousel-vue'
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true })
const logSlidesInView = (emblaApi) => { console.log(emblaApi.slidesInView())}
watch( emblaApi, (api) => { if (!api) return api.on('slidesInView', logSlidesInView) }, { immediate: true })</script>
<template> <div class="embla"> <div class="embla__viewport" ref="emblaRef"> <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> </div></template>import { createEffect, on } from 'solid-js'import useEmblaCarousel from 'embla-carousel-solid'
export function EmblaCarousel() { const [emblaRef, emblaApi] = useEmblaCarousel(() => ({ loop: true }))
const logSlidesInView = (emblaApi) => { console.log(emblaApi.slidesInView()) }
createEffect( on(emblaApi, (api) => { if (!api) return api.on('slidesInView', logSlidesInView) }) )
return ( <div class="embla"> <div class="embla__viewport" ref={emblaRef}> <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> </div> )}<script> import useEmblaCarousel from 'embla-carousel-svelte'
let emblaApi let options = { loop: true }
const logSlidesInView = (emblaApi) => { console.log(emblaApi.slidesInView()) }
const onInit = (event) => { emblaApi = event.detail emblaApi.on('slidesInView', logSlidesInView) }</script>
<div class="embla"> <div class="embla__viewport" on:emblaInit={onInit} use:useEmblaCarousel={{ options }} > <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></div>Note: Starting with Svelte 5, the on: event handlers have been
deprecated. However, on:emblainit will remain for backward
compatibility.
Removing event listeners
To remove an event listener, call the off method and pass the same callback reference that was originally used with on.
import EmblaCarousel from 'embla-carousel'
const wrapperNode = document.querySelector('.embla')const viewportNode = wrapperNode.querySelector('.embla__viewport')
const emblaApi = EmblaCarousel(viewportNode, { loop: true })
const logSlidesInViewOnce = (emblaApi) => { console.log(emblaApi.slidesInView()) emblaApi.off('slidesInView', logSlidesInViewOnce)}
emblaApi.on('slidesInView', logSlidesInViewOnce)<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></div>import React, { useEffect } from 'react'import useEmblaCarousel from 'embla-carousel-react'
export function EmblaCarousel() { const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true })
const logSlidesInViewOnce = (emblaApi) => { console.log(emblaApi.slidesInView()) emblaApi.off('slidesInView', logSlidesInViewOnce) }
useEffect(() => { if (!emblaApi) return emblaApi.on('slidesInView', logSlidesInViewOnce) }, [emblaApi])
return ( <div className="embla"> <div className="embla__viewport" ref={emblaRef}> <div className="embla__container"> <div className="embla__slide">Slide 1</div> <div className="embla__slide">Slide 2</div> <div className="embla__slide">Slide 3</div> </div> </div> </div> )}<script setup>import { watch } from 'vue'import useEmblaCarousel from 'embla-carousel-vue'
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true })
const logSlidesInViewOnce = (emblaApi) => { console.log(emblaApi.slidesInView()) emblaApi.off('slidesInView', logSlidesInViewOnce)}
watch( emblaApi, (api) => { if (!api) return api.on('slidesInView', logSlidesInViewOnce) }, { immediate: true })</script>
<template> <div class="embla"> <div class="embla__viewport" ref="emblaRef"> <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> </div></template>import { createEffect, on } from 'solid-js'import useEmblaCarousel from 'embla-carousel-solid'
export function EmblaCarousel() { const [emblaRef, emblaApi] = useEmblaCarousel(() => ({ loop: true }))
const logSlidesInViewOnce = (emblaApi) => { console.log(emblaApi.slidesInView()) emblaApi.off('slidesInView', logSlidesInViewOnce) }
createEffect( on(emblaApi, (api) => { if (!api) return api.on('slidesInView', logSlidesInViewOnce) }) )
return ( <div class="embla"> <div class="embla__viewport" ref={emblaRef}> <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> </div> )}<script> import useEmblaCarousel from 'embla-carousel-svelte'
let emblaApi let options = { loop: true }
const logSlidesInViewOnce = (emblaApi) => { console.log(emblaApi.slidesInView()) emblaApi.off('slidesInView', logSlidesInViewOnce) }
const onInit = (event) => { emblaApi = event.detail emblaApi.on('slidesInView', logSlidesInViewOnce) }</script>
<div class="embla"> <div class="embla__viewport" on:emblainit={onInit} use:useEmblaCarousel={{ options }} > <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></div>Note: Starting with Svelte 5, the on: event handlers have been
deprecated. However, on:emblainit will remain for backward
compatibility.
TypeScript
The EmblaEventType is obtained directly from the core package embla-carousel and used like so:
import EmblaCarousel, { EmblaCarouselType, EmblaEventType} from 'embla-carousel'
const wrapperNode = <HTMLElement>document.querySelector('.embla')const viewportNode = <HTMLElement>wrapperNode.querySelector('.embla__viewport')
const emblaApi = EmblaCarousel(viewportNode, { loop: true })
const logEmblaEvent = ( emblaApi: EmblaCarouselType, eventName: EmblaEventType): void => { console.log(`Embla just triggered ${eventName}!`)}
emblaApi.on('select', logEmblaEvent)<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></div>import React, { useEffect } from 'react'import { EmblaCarouselType, EmblaEventType } from 'embla-carousel'import useEmblaCarousel from 'embla-carousel-react'
export function EmblaCarousel() { const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true })
const logEmblaEvent = ( emblaApi: EmblaCarouselType, eventName: EmblaEventType ): void => { console.log(`Embla just triggered ${eventName}!`) }
useEffect(() => { if (!emblaApi) return emblaApi.on('select', logEmblaEvent) }, [emblaApi])
return ( <div className="embla"> <div className="embla__viewport" ref={emblaRef}> <div className="embla__container"> <div className="embla__slide">Slide 1</div> <div className="embla__slide">Slide 2</div> <div className="embla__slide">Slide 3</div> </div> </div> </div> )}If you're using pnpm, you need to install embla-carousel as a
devDependency when importing types from it like demonstrated above.
This is because even though embla-carousel-react has embla-carousel as
a dependency, pnpm makes nested dependencies inaccessible by design.
<script setup lang="ts">import { watch } from 'vue'import { EmblaCarouselType, EmblaEventType } from 'embla-carousel'import useEmblaCarousel from 'embla-carousel-vue'
const [emblaRef, emblaApi] = useEmblaCarousel({ loop: true })
const logEmblaEvent = ( emblaApi: EmblaCarouselType, eventName: EmblaEventType): void => { console.log(`Embla just triggered ${eventName}!`)}
watch( emblaApi, (api) => { if (!api) return api.on('select', logEmblaEvent) }, { immediate: true })</script>
<template> <div class="embla"> <div class="embla__viewport" ref="emblaRef"> <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> </div></template>If you're using pnpm, you need to install embla-carousel as a
devDependency when importing types from it like demonstrated above.
This is because even though embla-carousel-vue has embla-carousel as a
dependency, pnpm makes nested dependencies inaccessible by design.
import { createEffect, on } from 'solid-js'import { EmblaCarouselType, EmblaEventType } from 'embla-carousel'import useEmblaCarousel from 'embla-carousel-solid'
export function EmblaCarousel() { const [emblaRef, emblaApi] = useEmblaCarousel(() => ({ loop: true }))
const logEmblaEvent = ( emblaApi: EmblaCarouselType, eventName: EmblaEventType ): void => { console.log(`Embla just triggered ${eventName}!`) }
createEffect( on(emblaApi, (api) => { if (!api) return api.on('select', logEmblaEvent) }) )
return ( <div class="embla"> <div class="embla__viewport" ref={emblaRef}> <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> </div> )}If you're using pnpm, you need to install embla-carousel as a
devDependency when importing types from it like demonstrated above.
This is because even though embla-carousel-solid has embla-carousel as
a dependency, pnpm makes nested dependencies inaccessible by design.
<script lang="ts"> import { EmblaCarouselType, EmblaEventType, EmblaOptionsType } from 'embla-carousel' import useEmblaCarousel from 'embla-carousel-svelte'
let emblaApi: EmblaCarouselType let options: EmblaOptionsType = { loop: true }
const logEmblaEvent = ( emblaApi: EmblaCarouselType, eventName: EmblaEventType ): void => { console.log(`Embla just triggered ${eventName}!`) }
const onInit = (event: CustomEvent<EmblaCarouselType>): void => { emblaApi = event.detail emblaApi.on('select', logEmblaEvent) }</script>
<div class="embla"> <div class="embla__viewport" on:emblaInit={onInit} use:useEmblaCarousel={{ options }} > <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></div>Note: Starting with Svelte 5, the on: event handlers have been
deprecated. However, on:emblainit will remain for backward
compatibility.
If you're using pnpm, you need to install embla-carousel as a
devDependency when importing types from it like demonstrated above.
This is because even though embla-carousel-svelte has embla-carousel
as a dependency, pnpm makes nested dependencies inaccessible by design.
Reference
Below follows an exhaustive list of all Embla Carousel events together with information about how they work.
init
emblaApi.on('init', (emblaApi) => {})Runs when the carousel mounts for the first time. This only fires once which means that it won't fire when the carousel is re-initialized using the reInit method.
reInit
emblaApi.on('reInit', (emblaApi) => {})Runs when the reInit method is called. When the window is resized, Embla Carousel automatically calls the reInit method which will also fire this event.
destroy
emblaApi.on('destroy', (emblaApi) => {})Runs when the carousel has been destroyed using the destroy method. This only fires once and will be the last event the carousel fires.
select
emblaApi.on('select', (emblaApi) => {})/>
Runs when the selected scroll snap changes. The select event is triggered by drag interactions or the scrollNext, scrollPrev or scrollTo methods.
scroll
emblaApi.on('scroll', (emblaApi) => {})Runs when the carousel is scrolling. It might be a good idea to throttle this if you're doing expensive stuff in your callback function.
settle
emblaApi.on('settle', (emblaApi) => {})Runs when the carousel has settled after scroll has been triggered. Please note that this can take longer than you think when dragFree is enabled or when using slow transitions.
resize
emblaApi.on('resize', (emblaApi) => {})Runs when the carousel container or the slide sizes change. It's using ResizeObserver under the hood.
slidesInView
emblaApi.on('slidesInView', (emblaApi) => {})/>
Runs when any slide has entered or exited the viewport. This event is intended to be used together with the slidesInView and/or slidesNotInView methods.
slidesChanged
emblaApi.on('slidesChanged', (emblaApi) => {})Runs when slides are added to, or removed from the carousel container. It's using MutationObserver under the hood.
slideFocus
emblaApi.on('slideFocus', (emblaApi) => {})Runs when a slide receives focus. For example, when a focusable element like a button, link or input receives focus inside a slide.
pointerDown
emblaApi.on('pointerDown', (emblaApi) => {})Runs when the user has a pointer down on the carousel. It's triggered by a touchstart or a mousedown event.
pointerUp
emblaApi.on('pointerUp', (emblaApi) => {})Runs when the user has released the pointer from the carousel. It's triggered by a touchend or a mouseup event.