swiper
Swiper is a browser carousel engine for touch-first sliders, galleries, product rails, and full-screen presentations. It supplies drag physics, snapping, responsive layouts, RTL behavior, looping, keyboard and mouse-wheel control, virtual slides, effects, and accessibility helpers. You can use its DOM API directly, its React or Vue components, or registered Web Components. Version 14 is ESM-only and keeps optional behavior in separately imported modules and CSS.
Swiper earns its size when the carousel is a real interaction surface, not decorative overflow. For a simple product row or testimonial strip, start with CSS scroll snap or a smaller headless engine and avoid the upgrade and configuration surface.
Use it if
- You need a touch carousel with momentum, nested sliders, RTL support, and fine-grained gesture settings that would take substantial testing to reproduce
- You need one maintained package with direct DOM, React, Vue, and Web Component entry points
- Your design calls for linked thumbnail galleries, virtualized slide lists, autoplay, zoom, or presentation effects
- You can budget for library CSS and test the carousel with keyboards, screen readers, reduced motion, and real touch devices
- A horizontal list with CSS scroll snap and two buttons meets the requirement; Swiper's main JavaScript entry is 19.4 KB gzipped before its CSS, which is unnecessary machinery for that case
- You require old browser support; the README explicitly says Swiper focuses on modern apps and is not compatible with every platform
- You expect every feature to combine freely; the API says loop and effect cannot change at breakpoints, rewind must not be combined with loop, and several centered or grid options have their own incompatibilities
- You want a dependency that rarely changes integration details; Swiper has reached major version 14, and major upgrades have repeatedly changed imports, module registration, markup, and framework support
- You assume an accessibility module makes a carousel automatically accessible; A11y adds roles, labels, live-region behavior, and messages, but your content order, focus flow, autoplay controls, and reduced-motion behavior still need product-level review
Setup reality
Installation is one package, but a working slider is not one import. Version 14 is ESM-only. A vanilla build needs the required `.swiper`, `.swiper-wrapper`, and `.swiper-slide` structure, a JavaScript import, and at least `swiper/css`; missing the CSS commonly looks like a broken vertical list. Optional behavior is modular: import `Navigation`, `Pagination`, `Autoplay`, or other modules from `swiper/modules`, pass them in `modules`, and import the matching CSS files. `swiper/css/bundle` is easier but ships every module's styles. React and Vue have separate `swiper/react` and `swiper/vue` entry points, while Web Components require a client-side `register()` call. In server-rendered apps, keep initialization in a client component or browser-only hook because Swiper operates on the DOM. Containers also need a usable width and height; hidden tabs, modals, and late-loading fonts or images may require `swiper.update()` or observer settings. Loop mode rearranges slides and requires enough slides, plus counts even to `slidesPerGroup` and grid rows unless blank slides may be added. Breakpoints default to viewport width, not container width, and options that alter layout logic, including `loop` and `effect`, cannot be switched there. Autoplay defaults do not provide a visible pause control. Treat A11y as useful wiring, not an accessibility sign-off, and test focus, announcements, reduced motion, and keyboard operation yourself.
Patterns
Create a basic touch slidercreate-basic-slider
import Swiper from 'swiper';
import 'swiper/css';
const swiper = new Swiper('.swiper', {
slidesPerView: 1,
spaceBetween: 16,
});The container must contain `.swiper-wrapper` and `.swiper-slide` elements, and importing the core CSS is required for layout.
Add arrows and clickable paginationadd-navigation-pagination
import Swiper from 'swiper';
import { Navigation, Pagination } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
new Swiper('.swiper', {
modules: [Navigation, Pagination],
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
pagination: { el: '.swiper-pagination', clickable: true },
});Importing a module's JavaScript does not import its styles; include each CSS module or use `swiper/css/bundle`.
Change visible slides responsivelyconfigure-breakpoints
new Swiper('.swiper', {
slidesPerView: 1,
spaceBetween: 12,
breakpoints: {
640: { slidesPerView: 2, spaceBetween: 16 },
1024: { slidesPerView: 4, spaceBetween: 24 },
},
});Breakpoint keys use viewport width by default. Set `breakpointsBase: 'container'` for container-based behavior, which the API still labels beta.
Loop through a sufficiently large slide setenable-safe-looping
new Swiper('.swiper', {
slidesPerView: 3,
slidesPerGroup: 1,
loop: true,
loopAddBlankSlides: true,
});Loop rearranges slides. The count must be at least `slidesPerView + slidesPerGroup` and even to the group and grid-row counts, unless blank slides may be inserted.
Autoplay without permanently stopping after a swipeconfigure-autoplay
import { Autoplay } from 'swiper/modules';
import 'swiper/css/autoplay';
const swiper = new Swiper('.swiper', {
modules: [Autoplay],
autoplay: {
delay: 5000,
disableOnInteraction: false,
pauseOnMouseEnter: true,
},
});
document.querySelector('#pause').addEventListener('click', () => {
swiper.autoplay.paused ? swiper.autoplay.resume() : swiper.autoplay.pause();
});Provide a visible pause control and respect reduced-motion preferences; autoplay configuration alone does not satisfy those user needs.
React to the active slide changinglisten-to-slide-changes
const swiper = new Swiper('.swiper', {
on: {
slideChange(instance) {
console.log('logical index', instance.realIndex);
},
},
});Use `realIndex` when loop mode is enabled; `activeIndex` reflects Swiper's rearranged slide order.
Recalculate after changing slide markupupdate-dynamic-slides
const wrapper = document.querySelector('.swiper-wrapper');
wrapper.insertAdjacentHTML('beforeend', '<div class="swiper-slide">New</div>');
swiper.update();Call `update()` after manual DOM changes, or enable `observer` and related observer options if changes are frequent and their overhead is acceptable.
Move, disable, and clean up an instancecontrol-slider-programmatically
swiper.slideTo(3, 400);
swiper.disable();
swiper.enable();
// Before permanently removing the carousel:
swiper.destroy(true, true);Destroy instances created outside a framework before removing their DOM, especially in client-side navigation, to release listeners and inline styles.
Use Swiper in Reactbuild-react-carousel
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
export function Gallery({ images }) {
return (
<Swiper modules={[Navigation, Pagination]} navigation pagination={{ clickable: true }}>
{images.map((src) => (
<SwiperSlide key={src}><img src={src} alt="" /></SwiperSlide>
))}
</Swiper>
);
}In an SSR framework, place this in a client component or load it browser-side; Swiper's interaction layer depends on DOM APIs.
Virtualize a long React slide listrender-virtual-react-slides
import { Virtual } from 'swiper/modules';
import { Swiper, SwiperSlide } from 'swiper/react';
import 'swiper/css';
import 'swiper/css/virtual';
<Swiper modules={[Virtual]} virtual>
{items.map((item, index) => (
<SwiperSlide key={item.id} virtualIndex={index}>
{item.label}
</SwiperSlide>
))}
</Swiper>React handles virtual rendering here, but every slide still needs the correct `virtualIndex` so Swiper can map logical and rendered positions.
Enable keyboard control and customize announcementsenable-keyboard-a11y
import { A11y, Keyboard } from 'swiper/modules';
new Swiper('.swiper', {
modules: [A11y, Keyboard],
keyboard: { enabled: true, onlyInViewport: true },
a11y: {
containerRoleDescriptionMessage: 'Featured products carousel',
nextSlideMessage: 'Show next product',
prevSlideMessage: 'Show previous product',
},
});The A11y module supplies roles and messages, but you must still test focus order, meaningful image text, controls, and announcements with assistive technology.
Use native CSS scroll snap modeuse-css-scroll-mode
new Swiper('.swiper', {
cssMode: true,
slidesPerView: 'auto',
spaceBetween: 16,
});cssMode can perform better for simple sliders, but it does not support cube effects, several transition events, mouse dragging through `simulateTouch`, resistance, or every grouping behavior.
Alternatives
| Package | Registry | Pick it when |
|---|---|---|
| embla-carousel | npm | Choose it for a smaller headless carousel core when your design system should own all markup and controls |
| keen-slider | npm | Choose it for touch sliders with a compact API and official React, Vue, and Solid hooks |
| @splidejs/splide | npm | Choose it when accessible carousel defaults and straightforward declarative HTML matter more than Swiper's breadth of effects |