Blossom Carousel Logo
Migration Guides

Embla

Migrate from Embla Carousel to Blossom Carousel by moving layout and behavior back to native scrolling and CSS.

Embla creates a carousel engine around a viewport, container, options object, event system, and plugin API.

Blossom keeps the carousel as a native scroll container. Layout, snapping, alignment, spacing, direction, and responsive behavior belong in CSS.

Migration mindset

When moving from Embla to Blossom, migrate the carousel in three steps:

  1. Replace the Embla viewport and container structure with a single <BlossomCarousel> root.
  2. Move Embla options such as alignment, axis, slide sizing, and containment into CSS.
  3. Replace Embla API calls with Blossom methods, native scroll APIs, and browser observers.
Most Blossom migrations replace abstract JavaScript configuration with native expressions using CSS scroll snap, grid, sticky positioning, or native scrol API's.

Installation

Remove the Embla package for your framework and install the matching Blossom package.

npm uninstall embla-carousel-vue
npm install @blossom-carousel/vue

Import Blossom's core stylesheet once in your app.

import "@blossom-carousel/core/style.css";

Component structure

Embla usually needs a viewport element, a container element, and slide elements. With Blossom, the carousel root is the scroll container and the slides are direct children.

<script setup lang="ts">
import useEmblaCarousel from "embla-carousel-vue";import { BlossomCarousel } from "@blossom-carousel/vue";import "@blossom-carousel/core/style.css"; 
const [emblaRef] = useEmblaCarousel({ align: "start" });</script>

<template>
    <div class="embla" 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>    <BlossomCarousel class="carousel">        <div class="slide snap-start">Slide 1</div>        <div class="slide snap-start">Slide 2</div>        <div class="slide snap-start">Slide 3</div>    </BlossomCarousel></template>

Options

Embla options describe how the carousel engine should calculate slide positions. In Blossom, those same decisions are usually regular CSS on the scroll container and slides.

Embla conceptBlossom approach
axis: "x"Automatically handled by Blossom based on native overscroll.
align: "start"Use CSS scroll-snap-align: start on slides.
align: "center"Use CSS scroll-snap-align: center on slides.
containScrollUse native scroll bounds, padding, and scroll-padding.
slidesToScrollUse CSS scroll-snap-align within :nth-child(xn) or nest slides in groups.
dragFreeOmit scroll-snap-align or use scroll-snap-type: proximity.
direction: "rtl"Use dir="rtl" or CSS direction: rtl.
breakpointsUse CSS media queries and container queries.
loopPrefer bounded native scrolling, or use the experimental repeat pattern when an infinite effect is required. See Repeat example.
draggableUse CSS overflow: hidden on the carousel to disable dragging and scrolling.

For example, this Embla setup:

Embla
useEmblaCarousel({
  axis: "x",
  align: "center",
  containScroll: "trimSnaps",
  dragFree: false,
  breakpoints: {
    "(min-width: 768px)": { align: "start" },
  },
});

becomes CSS:

Blossom
.carousel {
  scroll-snap-type: inline mandatory;
  scroll-padding-inline: 1rem;
}

.slide {
  scroll-snap-align: center;
}

@media (min-width: 768px) {
  .slide {
    scroll-snap-align: start;
  }
}

Controls

Embla and Blossom both expose carousel controls through their respective APIs.

const [emblaRef, emblaApi] = useEmblaCarousel();emblaApi.scrollPrev();emblaApi.scrollNext();const blossom = ref(null);blossom.value.prev();blossom.value.next();

For direct slide navigation, use native scrolling on the target slide.

slides[index].scrollIntoView({
  behavior: "smooth",
  inline: "center",
  block: "nearest",
});

API

Embla commonly uses select events and selectedScrollSnap() to track the active slide.

With Blossom, the carousel is a real scroll container, so use browser APIs such as IntersectionObserver, scrollsnapchange, or scroll events depending on the browser support you need.

emblaApi.on("select", () => {  setSelectedIndex(emblaApi.selectedScrollSnap());});
carousel.addEventListener("scrollsnapchange", (event) => {  setSelectedIndex(event.detail.index);});
Or use IntersectionObserver for wider browser support

const slides = Array.from(carousel.children);const observer = new IntersectionObserver(   (entries) => {    const activeEntry = entries.find((entry) => entry.isIntersecting);    if (activeEntry) {      const index = slides.indexOf(activeEntry.target);      setSelectedIndex(index);    }  },  { root: carousel, threshold: 0.5 },);slides.forEach((slide) => observer.observe(slide));

Plugins

Embla plugins usually become either small Blossom wrappers or native platform features.

Embla plugin patternBlossom replacement
AutoplayUse a timer that calls next() or scrolls the next slide into view.
FadeUse CSS scroll-driven animations or intersection state.
Auto heightLet content define height, or update layout with ResizeObserver.
Class namesUse IntersectionObserver, scrollsnapchange, or scroll-state queries.
Wheel gesturesUse the browser's native scroll behavior where possible.

Keep plugin migrations small. Start from a working Blossom carousel, then add the behavior back only if the product still needs it.

Lifecycle

Embla's and Blossom's lifecycle methods are similar.

Blossom initializes automatically when using framework components.
const [emblaRef, emblaApi] = useEmblaCarousel();emblaApi.reInit();emblaApi.destroy();const blossom = Blossom(carouselElement);blossom.init();blossom.destroy();

Migration checklist

  • Remove Embla packages and install the matching Blossom package.
  • Import @blossom-carousel/core/style.css once.
  • Replace Embla viewport and container markup with <BlossomCarousel> and direct slide children.
  • Move slide sizing, alignment, gaps, snapping, direction, and breakpoints into CSS.
  • Replace scrollPrev() and scrollNext() with prev() and next().
  • Replace scrollTo() with scrollIntoView() or native scrollTo() on the carousel element.
  • Replace select listeners with browser observers or scroll snap events.
  • Rebuild only the Embla plugins your carousel still needs.

Next steps

  • Installation - Pick the package for your framework.
  • Snapping - Configure scroll snap behavior with CSS.
  • Buttons - Add previous and next controls.
  • Dots - Track the active slide with browser APIs.
  • Repeat - Explore an experimental repeating carousel pattern.