Splide
Splide builds a carousel from a root element, track, list, slide elements, options, extensions, and a JavaScript instance.
Blossom keeps the carousel as a native scroll container. Layout, slide sizing, gaps, snapping, and responsive behavior belong in CSS, while Blossom adds dragging and small convenience methods.
Migration mindset
When moving from Splide to Blossom, migrate the carousel in three steps:
- Replace the Splide root, track, and list structure with a single
<BlossomCarousel>root. - Move Splide options such as
perPage,gap,focus,direction, andbreakpointsinto CSS. - Replace Splide instance calls and events with Blossom methods, native scroll APIs, and browser observers.
Installation
Remove Splide and install the matching Blossom package for your framework.
npm uninstall @splidejs/vue-splide @splidejs/splide
npm install @blossom-carousel/vue
npm uninstall @splidejs/react-splide @splidejs/splide
npm install @blossom-carousel/react
npm uninstall @splidejs/svelte-splide @splidejs/splide
npm install @blossom-carousel/svelte
npm uninstall @splidejs/splide
npm install @blossom-carousel/core
Import Blossom's core stylesheet once in your app.
import "@blossom-carousel/core/style.css";
Component structure
Splide usually needs a root, track, list, and slide elements. Blossom uses one scroll container with direct slide children.
<script setup lang="ts">
import { Splide, SplideSlide } from "@splidejs/vue-splide";import "@splidejs/vue-splide/css";import { BlossomCarousel } from "@blossom-carousel/vue";import "@blossom-carousel/core/style.css";</script>
<template>
<Splide :options="{ perPage: 4, gap: '1rem' }"> <SplideSlide>Slide 1</SplideSlide> <SplideSlide>Slide 2</SplideSlide> <SplideSlide>Slide 3</SplideSlide> </Splide> <BlossomCarousel class="carousel grid! grid-auto-cols grid-cols-[calc((100% - 3rem) / 4)] gap-4"> <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>
import { Splide, SplideSlide } from "@splidejs/react-splide";import "@splidejs/react-splide/css";import { BlossomCarousel } from "@blossom-carousel/react";import "@blossom-carousel/core/style.css";
export function Carousel() {
return (
<Splide options={{ perPage: 4, gap: "1rem" }}> <SplideSlide>Slide 1</SplideSlide> <SplideSlide>Slide 2</SplideSlide> <SplideSlide>Slide 3</SplideSlide> </Splide> <BlossomCarousel className="carousel grid! grid-auto-cols grid-cols-[calc((100% - 3rem) / 4)] gap-4"> <div className="slide snap-start">Slide 1</div> <div className="slide snap-start">Slide 2</div> <div className="slide snap-start">Slide 3</div> </BlossomCarousel> );
}
<script>
import { Splide, SplideSlide } from "@splidejs/svelte-splide"; import "@splidejs/svelte-splide/css"; import BlossomCarousel from "@blossom-carousel/svelte"; import "@blossom-carousel/core/style.css";
const options = { perPage: 4, gap: "1rem" };</script>
<Splide {options}> <SplideSlide>Slide 1</SplideSlide> <SplideSlide>Slide 2</SplideSlide> <SplideSlide>Slide 3</SplideSlide></Splide><BlossomCarousel class="carousel grid! grid-auto-cols grid-cols-[calc((100% - 3rem) / 4)] gap-4"> <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><div class="splide"> <div class="splide__track"> <ul class="splide__list"> <li class="splide__slide">Slide 1</li> <li class="splide__slide">Slide 2</li> <li class="splide__slide">Slide 3</li> </ul> </div></div><blossom-carousel class="carousel grid! grid-auto-cols grid-cols-[calc((100% - 3rem) / 4)] gap-4"> <div class="slide snap-start">Slide 1</div> <div class="slide snap-start">Slide 2</div> <div class="slide snap-start">Slide 3</div></blossom-carousel>Options
Splide options describe slide movement, sizing, and generated controls. In Blossom, those choices usually become CSS and small browser APIs.
| Splide concept | Blossom approach |
|---|---|
perPage | Use CSS grid-auto-columns to define the relative width of slides. |
perMove | Use CSS scroll-snap-align within :nth-child(xn) or nest slides in groups. |
gap | Use CSS gap in a grid or flex layout. |
focus: "center" | Use scroll-snap-align: center. |
direction | Use dir="rtl" or CSS direction: rtl. |
breakpoints | Use CSS media queries and container queries. |
type: "loop" | Prefer bounded native scrolling, or use the experimental repeat pattern when an infinite effect is required. See Repeat example. |
drag: "free" | Omit scroll-snap-align or use scroll-snap-type: proximity. |
arrows | Use buttons that call prev() and next(). See Buttons example. |
pagination | Build dots from IntersectionObserver or scrollsnapchange. See Dots example. |
For example, this Splide setup:
new Splide(".splide", {
perPage: 1,
gap: "1rem",
focus: "center",
breakpoints: {
768: { perPage: 3 },
},
}).mount();
becomes CSS:
.carousel {
display: grid;
grid-auto-flow: column;
grid-auto-columns: 100%;
gap: 1rem;
scroll-snap-type: inline mandatory;
}
.slide {
scroll-snap-align: center;
}
@media (min-width: 768px) {
.carousel {
grid-auto-columns: calc((100% - 2rem) / 3);
}
}
Controls
Splide controls call methods on the Splide instance. Blossom exposes prev() and next() on the carousel element or component ref.
splide.go("<");splide.go(">");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
Splide commonly uses moved or active events to track the current 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.
splide.on("moved", (newIndex) => { setSelectedIndex(newIndex);});
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) { setSelectedIndex(slides.indexOf(activeEntry.target)); } }, { root: carousel, threshold: 0.5 },);slides.forEach((slide) => observer.observe(slide));Extensions
Splide extensions and built-in UI usually become small Blossom wrappers or native platform features.
| Splide pattern | Blossom replacement |
|---|---|
| Arrows | Buttons that call prev() and next(). |
| Pagination | Dots backed by IntersectionObserver or scrollsnapchange. |
| Autoplay | A timer that calls next() or scrolls the next slide into view. |
| Auto width | Let slide content size itself with CSS. |
| Auto height | Let content define height, or update layout with ResizeObserver. |
| Grid / rows | CSS grid. |
| Lazy load | Native loading="lazy" and content-visibility: auto. |
Keep extension migrations small. Start from a working Blossom carousel, then add behavior only if the product still needs it.
Lifecycle
Splide's and Blossom's lifecycle methods are similar.
splide.refresh();splide.destroy();blossom.init();blossom.destroy();Migration checklist
- Remove Splide packages and install the matching Blossom package.
- Import
@blossom-carousel/core/style.cssonce. - Replace Splide root, track, list, and slide wrappers with
<BlossomCarousel>and direct slide children. - Move slide sizing, gaps, snapping, direction, focus, and breakpoints into CSS.
- Replace
go("<")andgo(">")withprev()andnext(). - Replace
go(index)withscrollIntoView()or nativescrollTo(). - Replace Splide events with browser observers or scroll snap events.
- Rebuild only the Splide extensions 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.