Options
Embla Carousel provides a range of configuration options that let you control its behavior.
Usage
Options can be set via the constructor or as global defaults. If both are present, they're merged, and the constructor options override any conflicting global options.
Default options
Here's a list of all available options and their default values:
const defaultOptions = { active: true, align: 'center', axis: 'x', breakpoints: {}, container: null, containScroll: 'trimSnaps', direction: 'ltr', dragFree: false, draggable: true, dragThreshold: 10, duration: 25, focus: true, inViewThreshold: 0, inViewMargin: '0px', loop: false, resize: true, skipSnaps: false, slideChanges: true, slides: null, slidesToScroll: 1, ssr: [], startSnap: 0}Constructor options
The constructor options are the primary way to configure Embla Carousel. In the example below, the loop option is set to true:
import EmblaCarousel from 'embla-carousel'
const wrapperNode = document.querySelector('.embla')const viewportNode = wrapperNode.querySelector('.embla__viewport')
const emblaApi = EmblaCarousel(viewportNode, { loop: true })<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 from 'react'import useEmblaCarousel from 'embla-carousel-react'
export function EmblaCarousel() { const [emblaRef] = useEmblaCarousel({ loop: true })
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 useEmblaCarousel from 'embla-carousel-vue'
const [emblaRef] = useEmblaCarousel({ loop: 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 useEmblaCarousel from 'embla-carousel-solid'
export function EmblaCarousel() { const [emblaRef] = useEmblaCarousel(() => ({ loop: true }))
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 options = { loop: true }</script>
<div class="embla"> <div class="embla__viewport" 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>Global options
Setting global options applies them to all carousels, overriding Embla's default options with your own. In the example below, the loop option is set to true:
import EmblaCarousel from 'embla-carousel'
EmblaCarousel.globalOptions = { loop: true }
const wrapperNode = document.querySelector('.embla')const viewportNode = wrapperNode.querySelector('.embla__viewport')
const emblaApi = EmblaCarousel(viewportNode, { align: 'start' })<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 from 'react'import useEmblaCarousel from 'embla-carousel-react'
useEmblaCarousel.globalOptions = { loop: true }
export function EmblaCarousel() { const [emblaRef] = useEmblaCarousel({ align: 'start' })
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 useEmblaCarousel from 'embla-carousel-vue'
useEmblaCarousel.globalOptions = { loop: true }
const [emblaRef] = useEmblaCarousel({ align: 'start' })</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 useEmblaCarousel from 'embla-carousel-solid'
useEmblaCarousel.globalOptions = { loop: true }
export function EmblaCarousel() { const [emblaRef] = useEmblaCarousel(() => ({ align: 'start' }))
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'
useEmblaCarousel.globalOptions = { loop: true }
let options = { align: 'start' }</script>
<div class="embla"> <div class="embla__viewport" 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>Assign global options before initializing any carousel and only once. Re-assigning them can lead to confusing code and unexpected behavior.
Changing options
It's possible to update the options passed to the Embla Carousel constructor after initialization using the reInit method.
In the React, Vue, Solid, and Svelte wrappers, you can provide reactive options, and the carousel will automatically reinitialize whenever they change. Here are some examples:
import EmblaCarousel from 'embla-carousel'
const wrapperNode = document.querySelector('.embla')const viewportNode = wrapperNode.querySelector('.embla__viewport')
let loopEnabled = trueconst emblaApi = EmblaCarousel(viewportNode, { loop: loopEnabled })
function toggleLoop() { loopEnabled = !loopEnabled emblaApi.reInit({ loop: loopEnabled })}<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, { useState } from 'react'import useEmblaCarousel from 'embla-carousel-react'
export function EmblaCarousel() { const [options, setOptions] = useState({ loop: true }) const [emblaRef] = useEmblaCarousel(options)
const toggleLoop = () => { setOptions((currentOptions) => ({ ...currentOptions, loop: !currentOptions.loop })) }
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 { ref } from 'vue'import useEmblaCarousel from 'embla-carousel-vue'
const options = ref({ loop: true })const [emblaRef] = useEmblaCarousel(options)
const toggleLoop = () => { options.value = { ...options.value, loop: !options.value.loop }}</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 { createSignal } from 'solid-js'import useEmblaCarousel from 'embla-carousel-solid'
export function EmblaCarousel() { const [options, setOptions] = createSignal({ loop: true }) const [emblaRef] = useEmblaCarousel(() => options())
const toggleLoop = () => { setOptions((currentOptions) => ({ ...currentOptions, loop: !currentOptions.loop })) }
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 options = { loop: true }
const toggleLoop = () => { options = { ...options, loop: !options.loop } }</script>
<div class="embla"> <div class="embla__viewport" 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>TypeScript
The EmblaOptionsType comes directly from the core package embla-carousel and can be used as follows:
import EmblaCarousel, { EmblaOptionsType } from 'embla-carousel'
const wrapperNode = <HTMLElement>document.querySelector('.embla')const viewportNode = <HTMLElement>wrapperNode.querySelector('.embla__viewport')
const options: EmblaOptionsType = { loop: true }const emblaApi = EmblaCarousel(viewportNode, options)<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, { useState } from 'react'import { EmblaOptionsType } from 'embla-carousel'import useEmblaCarousel from 'embla-carousel-react'
export function EmblaCarousel() { const [options, setOptions] = useState<EmblaOptionsType>({ loop: true }) const [emblaRef] = useEmblaCarousel(options)
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 { ref } from 'vue'import { EmblaOptionsType } from 'embla-carousel'import emblaCarouselVue from 'embla-carousel-vue'
const options = ref<EmblaOptionsType>({ loop: true })const [emblaRef] = emblaCarouselVue(options)</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 { createSignal } from 'solid-js'import { EmblaOptionsType } from 'embla-carousel'import useEmblaCarousel from 'embla-carousel-solid'
export function EmblaCarousel() { const [options, setOptions] = createSignal<EmblaOptionsType>({ loop: true }) const [emblaRef] = useEmblaCarousel(options)
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 { EmblaOptionsType } from 'embla-carousel' import useEmblaCarousel from 'embla-carousel-svelte'
let options: EmblaOptionsType = { loop: true }</script>
<div class="embla"> <div class="embla__viewport" 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>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 options and their default values.
active
booleantrue{ active: false | true }Setting this to false will not activate or deactivate the carousel. Useful when used together with the breakpoints option to toggle the carousel active/inactive depending on media queries.
align
string | functioncenter{ align: 'start' | 'center' | 'end' | (viewSize, snapSize, index) => 40 }Align the slides relative to the carousel viewport. Use one of the predefined alignments start, center or end. Alternatively, provide your own callback to fully customize the alignment.
axis
stringx{ axis: 'x' | 'y' }Choose scroll axis between x and y. Remember to stack your slides horizontally or vertically using CSS to match this option.
breakpoints
EmblaOptionsType{}{ loop: true, '(min-width: 768px)': { loop: false }}An object with options that will be applied for a given breakpoint by overriding the options at the root level.
Note: If multiple queries match, they will be merged. And when breakpoint options clash, the last one in the list has precedence.
container
string | HTMLElement | nullnull{ container: '.embla__container' | document.querySelector('.embla__container') }Enables choosing a custom container element which holds the slides. By default, Embla will choose the first direct child element of the root element. Provide either a valid CSS selector string or a HTML element.
containScroll
false | stringtrimSnaps{ containScroll: false | 'trimSnaps' | 'keepSnaps' }Clear leading and trailing empty space that causes excessive scrolling. Use trimSnaps to only use snap points that trigger scrolling or keepSnaps to keep them.
Note: When this is active, it will override alignments applied by the
align option for enough slides at the start and the end of
the carousel, in order to cover the leading and trailing space.
direction
stringltr{ direction: 'ltr' | 'rtl' }Choose content direction between ltr and rtl.
Note: When using rtl, the content direction also has to be set to RTL
using the HTML dir
attribute.
dragFree
booleanfalse{ dragFree: true | false }Enables momentum scrolling. The duration of the continued scrolling is proportional to how vigorous the drag gesture is.
draggable
booleantrue{ draggable: true | false }Enables for scrolling the carousel with mouse and touch interactions. Set this to false to disable drag events.
dragThreshold
number10{ dragThreshold: 10 }Drag threshold in pixels. This only affects when clicks are fired and not. In contrast to other carousel libraries, it will not affect when dragging of the carousel starts.
Note: Browsers handle touch events differently than mouse events. Browsers won't fire the click event when a touch event includes an accidental slight swipe gesture. This is why this threshold only works for mouse events.
duration
number25{ duration: 25 }Set scroll duration when triggered by any of the API methods. Higher numbers enables slower scrolling. Drag interactions are not affected because duration is then determined by the drag force.
Note: Duration is not in milliseconds because Embla uses an attraction
physics simulation when scrolling instead of easings. Only values between
20-60 are recommended.
focus
booleantrue{ focus: true | false }Embla automatically watches the slides for focus events. The default callback fires the slidefocus event and scrolls to the focused element. Set this to false to disable this behaviour.
inViewThreshold
IntersectionObserverInit.threshold0{ inViewThreshold: 0 }This is the Intersection Observer threshold option that will be applied to all slides.
inViewMargin
IntersectionObserverInit.rootMargin0px{ inViewMargin: '0px -10px 0px 0px' }Sets the Intersection Observer's rootMargin value, which is applied to all slides.
Note: When loop is enabled, expanding the observer's rectangle with
rootMargin may not behave as expected for some slides because their
positions shift to create the looping effect. This option is primarily
intended to offset padding-based spacing between slides, as shown in the
Slide Gaps guide.
loop
booleanfalse{ loop: true | false }Enables infinite looping. Embla will apply translateX or translateY to the slides that need to change position in order to create the loop effect.
Embla automatically falls back to false if slide content isn't enough to
create the loop effect without visible glitches.
resize
booleantrue{ resize: true | false }Embla automatically watches the container and slides for size changes and runs reinit when any size has changed. Set this to false to disable this behaviour.
skipSnaps
booleanfalse{ skipSnaps: true | false }Allow the carousel to skip scroll snaps if it's dragged vigorously. Note that this option will be ignored if the dragFree option is set to true.
slideChanges
booleantrue{ slideChanges: true | false }Embla automatically watches the container for added and/or removed slides and runs reinit if needed. Set this to false to disable this behaviour.
slides
string | HTMLElement[] | NodeListOf<HTMLElement> | nullnull{ slides: '.embla__slide' | Array.from(document.querySelectorAll('.embla__slide')) }Enables using custom slide elements. By default, Embla will choose all direct child elements of its container. Provide either a valid CSS selector string or a nodeList/array containing HTML elements.
Note: Even though it's possible to provide custom slide elements, they still have to be direct descendants of the carousel container.
Warning: If you place elements inside the carousel container that aren't
slides, they either shouldn't have any size, or should be detached from the
document flow with position: absolute or similar.
slidesToScroll
string | number1{ slidesToScroll: 1 | 2 | 'auto' }Specifies how many slides advance per navigation action (buttons, dots, or drag).
- Number — scrolls a fixed number of slides (e.g.
2→ two slides per move). auto— scrolls the number of slides currently visible in the viewport.
With variable-width slides and auto, the group size updates dynamically based on the slides that fit within the viewport.
ssr
number[][]{ ssr: [80, 80, 80, 40] }Defines slide sizes (as percentages) for server-side rendering (SSR), allowing Embla to calculate scroll snaps without client-side measurements. Each value corresponds to a slide's size in order — percentage of viewport width for horizontal carousels, or height for vertical ones.
startSnap
number0{ startSnap: 0 }Set the initial scroll snap to the given number. First snap index starts at 0. Please note that this is not necessarily equal to the number of slides when used together with the slidesToScroll option.