# Swiper

> Migrate from Swiper to Blossom Carousel by replacing Swiper modules, wrappers, and configuration with native scrolling and CSS.

Swiper provides a large carousel system with framework components, modules, configuration options, lifecycle events, and built-in effects.

Blossom keeps the carousel as a native scroll container. Layout, snapping, spacing, breakpoints, and many effects move back into CSS, while Blossom adds dragging and small convenience methods.

## Migration mindset

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

1. Replace `<Swiper>` and `<SwiperSlide>` with a single `<BlossomCarousel>` root and direct children.
2. Move Swiper options such as `slidesPerView`, `spaceBetween`, `direction`, and `breakpoints` into CSS.
3. Replace Swiper modules and events with Blossom methods, native scroll APIs, and browser observers.

<note color="neutral" icon="i-lucide-lightbulb">

Most Swiper migrations become smaller when you start with the markup and CSS first, then add back only the behavior the product still needs.

</note>

## Installation

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

<code-group sync="migration-code-style">

```bash [Vue]
npm uninstall swiper
npm install @blossom-carousel/vue
```

```bash [React]
npm uninstall swiper
npm install @blossom-carousel/react
```

```bash [Svelte]
npm uninstall swiper
npm install @blossom-carousel/svelte
```

```bash [Core]
npm uninstall swiper
npm install @blossom-carousel/core
```

</code-group>

Import the stylesheet from your framework package once in your app.

<code-group>

```js [Vue]
import "@blossom-carousel/vue/style.css";
```

```js [React]
import "@blossom-carousel/react/style.css";
```

```js [Svelte]
import "@blossom-carousel/svelte/style.css";
```

```js [Core]
import "@blossom-carousel/core/style.css";
```

```js [Web]
import "@blossom-carousel/web/style.css";
```

</code-group>

## Component structure

Swiper wraps slides in framework-specific components. Blossom uses the carousel root as the scroll container and treats direct children as slides.

<code-group sync="migration-code-style">

```vue [Vue]
<script setup lang="ts">
import { Swiper, SwiperSlide } from "swiper/vue"; // [!code --]
import "swiper/css"; // [!code --]
import { BlossomCarousel } from "@blossom-carousel/vue"; // [!code ++]
import "@blossom-carousel/vue/style.css"; // [!code ++]
</script>

<template>
  <Swiper :slides-per-view="4" :space-between="16"> <!-- [!code --] -->
    <SwiperSlide>Slide 1</SwiperSlide> <!-- [!code --] -->
    <SwiperSlide>Slide 2</SwiperSlide> <!-- [!code --] -->
    <SwiperSlide>Slide 3</SwiperSlide> <!-- [!code --] -->
  </Swiper> <!-- [!code --] -->
  <BlossomCarousel class="carousel grid! grid-auto-cols grid-cols-[25%] gap-4"> <!-- [!code ++] -->
    <div class="slide snap-center">Slide 1</div> <!-- [!code ++] -->
    <div class="slide snap-center">Slide 2</div> <!-- [!code ++] -->
    <div class="slide snap-center">Slide 3</div> <!-- [!code ++] -->
  </BlossomCarousel> <!-- [!code ++] -->
</template>
```

```tsx [React]
import { Swiper, SwiperSlide } from "swiper/react"; // [!code --]
import "swiper/css"; // [!code --]
import { BlossomCarousel } from "@blossom-carousel/react"; // [!code ++]
import "@blossom-carousel/react/style.css"; // [!code ++]

export function Carousel() {
  return (
    <Swiper slidesPerView={1.25} spaceBetween={16}> {/* [!code --] */}
      <SwiperSlide>Slide 1</SwiperSlide> {/* [!code --] */}
      <SwiperSlide>Slide 2</SwiperSlide> {/* [!code --] */}
      <SwiperSlide>Slide 3</SwiperSlide> {/* [!code --] */}
    </Swiper> {/* [!code --] */}
    <BlossomCarousel className="carousel grid! grid-auto-cols grid-cols-[25%] gap-4"> {/* [!code ++] */}
      <div className="slide snap-start">Slide 1</div> {/* [!code ++] */}
      <div className="slide snap-start">Slide 2</div> {/* [!code ++] */}
      <div className="slide snap-start">Slide 3</div> {/* [!code ++] */}
    </BlossomCarousel> {/* [!code ++] */}
  );
}
```

```svelte [Svelte]
<script>
  import { Swiper, SwiperSlide } from "swiper/svelte"; // [!code --]
  import "swiper/css"; // [!code --]
  import { BlossomCarousel } from "@blossom-carousel/svelte"; // [!code ++]
  import "@blossom-carousel/svelte/style.css"; // [!code ++]
</script>

<Swiper slidesPerView={1.25} spaceBetween={16}> <!-- [!code --] -->
  <SwiperSlide>Slide 1</SwiperSlide> <!-- [!code --] -->
  <SwiperSlide>Slide 2</SwiperSlide> <!-- [!code --] -->
  <SwiperSlide>Slide 3</SwiperSlide> <!-- [!code --] -->
</Swiper> <!-- [!code --] -->
<BlossomCarousel class="carousel grid! grid-auto-cols grid-cols-[25%] gap-4"> <!-- [!code ++] -->
  <div class="slide snap-start">Slide 1</div> <!-- [!code ++] -->
  <div class="slide snap-start">Slide 2</div> <!-- [!code ++] -->
  <div class="slide snap-start">Slide 3</div> <!-- [!code ++] -->
</BlossomCarousel> <!-- [!code ++] -->
```

```html [Core]
<div class="swiper"> <!-- [!code --] -->
  <div class="swiper-wrapper"> <!-- [!code --] -->
    <div class="swiper-slide">Slide 1</div> <!-- [!code --] -->
    <div class="swiper-slide">Slide 2</div> <!-- [!code --] -->
    <div class="swiper-slide">Slide 3</div> <!-- [!code --] -->
  </div> <!-- [!code --] -->
</div> <!-- [!code --] -->
<div class="carousel grid! grid-auto-cols grid-cols-[25%] gap-4"> <!-- [!code ++] -->
  <div class="slide snap-start">Slide 1</div> <!-- [!code ++] -->
  <div class="slide snap-start">Slide 2</div> <!-- [!code ++] -->
  <div class="slide snap-start">Slide 3</div> <!-- [!code ++] -->
</div> <!-- [!code ++] -->

<script type="module">
  import Swiper from "swiper"; // [!code --]
  import "swiper/css"; // [!code --]
  import { Blossom } from "@blossom-carousel/core"; // [!code ++]
  import "@blossom-carousel/core/style.css"; // [!code ++]

  const swiper = new Swiper(".swiper", { // [!code --]
    slidesPerView: 1.25, // [!code --]
    spaceBetween: 16, // [!code --]
  }); // [!code --]

  const carouselElement = document.querySelector(".carousel"); // [!code ++]
  const blossom = Blossom(carouselElement); // [!code ++]
  blossom.init(); // [!code ++]
</script>
```

</code-group>

## Options

Swiper options describe how its engine should size slides, attach modules, and calculate movement. In Blossom, most of those choices become CSS or small browser APIs.

<table>
<thead>
  <tr>
    <th>
      Swiper concept
    </th>
    
    <th>
      Blossom approach
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      <code>
        slidesPerView
      </code>
    </td>
    
    <td>
      Use CSS <code>
        grid-auto-columns
      </code>
      
       to define the relative width of slides.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        spaceBetween
      </code>
    </td>
    
    <td>
      Use CSS <code>
        gap
      </code>
      
       in a <code>
        grid
      </code>
      
       or <code>
        flex
      </code>
      
       layout.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        direction: "horizontal"
      </code>
    </td>
    
    <td>
      Automatically handled by Blossom based on native overscroll.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        centeredSlides
      </code>
    </td>
    
    <td>
      Use CSS <code>
        scroll-snap-align: center
      </code>
      
       on slides.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        breakpoints
      </code>
    </td>
    
    <td>
      Use CSS media queries and container queries.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        loop
      </code>
    </td>
    
    <td>
      Prefer bounded native scrolling, or use the experimental repeat pattern when an infinite effect is required. See <a href="/docs/examples/experimental/repeat">
        Repeat example
      </a>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        freeMode
      </code>
    </td>
    
    <td>
      Omit <code>
        scroll-snap-align
      </code>
      
       or use <code>
        scroll-snap-type: proximity
      </code>
      
      .
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        allowTouchMove
      </code>
    </td>
    
    <td>
      Use CSS <code>
        overflow: hidden
      </code>
      
       on the carousel to disable dragging and scrolling.
    </td>
  </tr>
  
  <tr>
    <td>
      <code>
        effect
      </code>
      
       modules
    </td>
    
    <td>
      Use CSS transforms, scroll-driven animations, or intersection state.
    </td>
  </tr>
</tbody>
</table>

For example, this Swiper setup:

```js [Swiper]
const swiperOptions = {
  slidesPerView: 1,
  spaceBetween: 16,
  centeredSlides: false,
  breakpoints: {
    768: { slidesPerView: 3, centeredSlides: true },
  },
};
```

becomes CSS:

```css [Blossom]
.carousel {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 100%;
  gap: 1rem;
  scroll-snap-type: inline mandatory;
}

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

@media (min-width: 768px) {
  .carousel {
    grid-auto-columns: calc((100% - 2rem) / 3);
  }

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

## Controls

Swiper controls call methods on a Swiper instance. Blossom provides `<BlossomPrev>`, `<BlossomNext>`, and `<BlossomDots>` components that link to the carousel through matching `id` and `for` props.

```ts
swiper.slidePrev(); // [!code --]
swiper.slideNext(); // [!code --]
```

```html
<BlossomCarousel id="gallery" class="carousel">
  <div data-blossom-slide class="slide">Slide 1</div>
  ...
</BlossomCarousel>

<div class="controls">
  <BlossomPrev for="gallery" />
  <BlossomNext for="gallery" />
</div>

<BlossomDots for="gallery" />
```

For direct slide navigation, call `goTo(index)` on the carousel. It scrolls to the slide at that zero-based index, counting slides marked with `data-blossom-slide`. Reach the carousel through a template ref in Vue, a `ref` in React, `bind:this` in Svelte, or the `<blossom-carousel>` element itself.

```ts
carousel.goTo(index);
carousel.goTo(index, { behavior: "instant" });
```

## API

Swiper commonly uses events such as `slideChange` and properties such as `activeIndex`.

With Blossom, read the active slide from `useBlossomNavigation` in Vue, React, and Svelte. Pass the carousel `id`; it returns `activeIndex`, `count`, `canPrev`, and `canNext`, and updates during drag and free scrolling too.

```ts
swiper.on("slideChange", () => { // [!code --]
  setSelectedIndex(swiper.activeIndex); // [!code --]
}); // [!code --]

const nav = useBlossomNavigation("gallery"); // [!code ++]
```

In React and Svelte, read `nav.activeIndex`. In Vue, `nav` is a ref, so read `nav.value.activeIndex` in script or `nav.activeIndex` in the template. Don't destructure the result in Svelte; that loses reactivity.

With Web Components, listen for the native `scrollsnapchange` event where it's supported:

```ts
const slides = Array.from(carousel.querySelectorAll("[data-blossom-slide]"));

carousel.addEventListener("scrollsnapchange", (event) => {
  setSelectedIndex(slides.indexOf(event.snapTargetInline));
});
```

## Modules

Swiper modules usually become either native browser features or small code that calls Blossom methods.

<table>
<thead>
  <tr>
    <th>
      Swiper module pattern
    </th>
    
    <th>
      Blossom replacement
    </th>
  </tr>
</thead>

<tbody>
  <tr>
    <td>
      Navigation
    </td>
    
    <td>
      <code>
        <BlossomPrev>
      </code>
      
       and <code>
        <BlossomNext>
      </code>
      
       components.
    </td>
  </tr>
  
  <tr>
    <td>
      Pagination
    </td>
    
    <td>
      <code>
        <BlossomDots>
      </code>
      
       with <code>
        data-blossom-slide
      </code>
      
       on slides.
    </td>
  </tr>
  
  <tr>
    <td>
      Autoplay
    </td>
    
    <td>
      A manual timer that scrolls the next slide into view.
    </td>
  </tr>
  
  <tr>
    <td>
      Keyboard
    </td>
    
    <td>
      Handled natively by the browser.
    </td>
  </tr>
  
  <tr>
    <td>
      Mousewheel
    </td>
    
    <td>
      Native scrolling where possible.
    </td>
  </tr>
  
  <tr>
    <td>
      Grid
    </td>
    
    <td>
      CSS grid.
    </td>
  </tr>
  
  <tr>
    <td>
      Effect Fade / Cards / Coverflow
    </td>
    
    <td>
      CSS scroll-driven animations or intersection state.
    </td>
  </tr>
</tbody>
</table>

Start with a working native-scroll carousel before rebuilding effects. Many Swiper effects are visual polish, not required carousel mechanics.

## Lifecycle

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

<note color="neutral" icon="i-lucide-lightbulb">

Blossom initializes automatically when using framework components.

</note>

```ts
swiper.update(); // [!code --]
swiper.destroy(); // [!code --]
blossom.init(); // [!code ++]
blossom.destroy(); // [!code ++]
```

## Migration checklist

- Remove Swiper packages and install the matching Blossom package.
- Import the stylesheet from your framework package once (for example `@blossom-carousel/vue/style.css`).
- Replace `<Swiper>` and `<SwiperSlide>` with `<BlossomCarousel>` and direct slide children.
- Move slide sizing, gaps, snapping, direction, and breakpoints into CSS.
- Replace `slidePrev()` and `slideNext()` with `<BlossomPrev>` and `<BlossomNext>`.
- Replace `slideTo()` with `goTo(index)` on the carousel.
- Replace Swiper events with `useBlossomNavigation` (or `scrollsnapchange` for Web Components).
- Rebuild only the Swiper modules your carousel still needs.

## Next steps

- [Installation](/docs/getting-started/installation) - Pick the package for your framework.
- [Snapping](/docs/examples/basic/snap) - Configure scroll snap behavior with CSS.
- [Buttons](/docs/examples/basic/buttons) - Add previous and next controls.
- [Dots](/docs/examples/basic/dots) - Add dot markers with `<BlossomDots>`.
- [Repeat](/docs/examples/experimental/repeat) - Explore an experimental repeating carousel pattern.
