Swiper is a free, open-source, modern touch slider for the web. It powers carousels, galleries, sliders, and full-screen swipe interfaces with hardware-accelerated transitions and native-feeling touch behavior — and it works with plain HTML/JavaScript, React, Vue, or as a standalone web component, without depending on jQuery or any other library.
This guide takes you from a first slider on the page to advanced multi-module setups: pagination, navigation arrows, autoplay, looping, responsive breakpoints, transition effects, zoom, thumbnails, virtual slides, and more. Every option, method, and class name in this guide is taken directly from Swiper's source.
Note
This guide covers Swiper v12.2.0. Swiper is tree-shakeable and modular: you only ship the features you actually import. Most of this guide is about choosing which modules and options you need.
1. Quick Start
The fastest way to see a working slider. Swiper needs three things: a small, specific HTML structure, its CSS, and a one-line initialization.
1.1 The required HTML structure
Swiper expects a container, a wrapper, and slides — with these exact class names:
<!-- Slider main container -->
<div class="swiper">
<!-- Additional required wrapper -->
<div class="swiper-wrapper">
<!-- Slides -->
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
</div>
</div>
Important
The class names
swiper,swiper-wrapper, andswiper-slideare the defaults Swiper looks for and styles. You can change them with thewrapperClass,slideClass, andcontainerModifierClassoptions, but if you do, you must also adapt the CSS. Most projects keep the defaults.
1.2 Minimal working example (CDN)
You can run Swiper with no build tooling by loading the bundle from a CDN:
<!DOCTYPE html>
<html>
<head>
<!-- Swiper bundle CSS (includes all module styles) -->
<link
rel="stylesheet"
href="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.css"
/>
<style>
.swiper { width: 600px; height: 300px; }
.swiper-slide {
display: flex;
align-items: center;
justify-content: center;
background: #f1f1f1;
font-size: 2rem;
}
</style>
</head>
<body>
<div class="swiper">
<div class="swiper-wrapper">
<div class="swiper-slide">Slide 1</div>
<div class="swiper-slide">Slide 2</div>
<div class="swiper-slide">Slide 3</div>
</div>
</div>
<!-- Swiper bundle JS (includes all modules) -->
<script src="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.js"></script>
<script>
const swiper = new Swiper('.swiper', {
// optional parameters
direction: 'horizontal',
loop: true,
});
</script>
</body>
</html>
Open the page and drag the slides with your mouse or finger. That is a complete, working Swiper.
Tip
The bundle build (
swiper-bundle.*) ships every module and is the easiest way to start. Once your project is in a bundler (Vite, webpack, Rollup), switch to the core + per-module imports shown in Installation so you only ship what you use.
1.3 You must give the slider a size
Swiper does not invent a height for you. If your slider looks invisible or collapsed, the most common cause is that .swiper has no height. Set a size in CSS:
.swiper {
width: 100%;
height: 400px;
}
Warning
A Swiper with no height is the single most frequent "it's not working" issue. Either give
.swiperan explicitheight, or use theautoHeight: trueoption so the container grows to fit the active slide.
2. Installation
2.1 Install from npm
npm
npm install swiper
yarn
yarn add swiper
pnpm
pnpm add swiper
The package is published as swiper. It includes TypeScript types out of the box — no separate @types/swiper package is needed.
Note
Swiper requires Node.js >= 4.7.0 to install (per the package's
enginesfield). In practice any modern Node version works; this floor only affects the install step, not the browser runtime.
2.2 Import core + only the modules you need (recommended)
This is the tree-shakeable path. Import the Swiper core class, the modules you want, and their CSS:
// import Swiper core and required modules
import Swiper from 'swiper';
import { Navigation, Pagination } from 'swiper/modules';
// import Swiper styles
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
// init Swiper
const swiper = new Swiper('.swiper', {
modules: [Navigation, Pagination],
// ... other parameters
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
pagination: {
el: '.swiper-pagination',
},
});
The available import entry points come straight from the package's exports map:
| Import | What it provides |
|---|---|
swiper |
The Swiper core class (default export) |
swiper/modules |
All feature modules (Navigation, Pagination, Autoplay, …) |
swiper/bundle |
Core + all modules pre-installed (no modules array needed) |
swiper/css |
Core styles (always required) |
swiper/css/bundle |
All module styles in one file |
swiper/css/navigation, swiper/css/pagination, … |
Per-module styles |
swiper/react |
React components (Swiper, SwiperSlide) |
swiper/vue |
Vue components |
swiper/element |
The <swiper-container> web component registration |
Tip
Always import
swiper/css(the core styles). Then add oneswiper/css/<module>import per module that has its own styling — currently:a11y,autoplay,controller, theeffect-*modules,free-mode,grid,hash-navigation,history,keyboard,manipulation,mousewheel,navigation,pagination,parallax,scrollbar,thumbs,virtual, andzoom.
2.3 Use the all-in-one bundle
If you don't care about bundle size (e.g. a quick prototype), import the bundle and skip the modules array entirely:
import Swiper from 'swiper/bundle';
import 'swiper/css/bundle';
const swiper = new Swiper('.swiper', {
// every module is already available
loop: true,
navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
pagination: { el: '.swiper-pagination' },
});
2.4 Use from a CDN (no build step)
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.css" />
<script src="https://cdn.jsdelivr.net/npm/swiper@12/swiper-bundle.min.js"></script>
When loaded this way, Swiper is available as a global, and all modules are already installed.
3. Everyday Usage
This section covers the tasks you'll reach for in almost every project.
3.1 Initialize a slider
const swiper = new Swiper(container, options);
container— a CSS selector string (e.g.'.swiper') or anHTMLElement.options— an optional object of parameters.
The returned swiper object is the live instance: you read its state (swiper.activeIndex, swiper.isEnd) and drive it (swiper.slideNext()) through it.
Note
If your selector matches multiple elements, Swiper initializes only the first match. To init many sliders, loop over the elements yourself and call
new Swiper(el, options)for each.
3.2 Set how many slides are visible
const swiper = new Swiper('.swiper', {
slidesPerView: 3, // show 3 slides at once
spaceBetween: 20, // 20px gap between slides
});
slidesPerView(default1) — number of slides visible at the same time. Can be a number, a fractional number (e.g.3.5to hint the next slide), or'auto'to let each slide's own width decide.spaceBetween(default0) — distance between slides, in px.
// Let each slide size itself (e.g. variable-width cards)
const swiper = new Swiper('.swiper', {
slidesPerView: 'auto',
spaceBetween: 10,
});
Tip
Use a fractional
slidesPerViewlike1.2or3.3to deliberately reveal a sliver of the next slide — a strong visual cue that the carousel is swipeable.
3.3 Change direction (horizontal / vertical)
const swiper = new Swiper('.swiper', {
direction: 'vertical', // default is 'horizontal'
});
Vertical sliders need an explicit height on .swiper to have room to scroll.
3.4 Center the active slide
const swiper = new Swiper('.swiper', {
slidesPerView: 3,
centeredSlides: true,
});
3.5 Navigate from your own code
Every navigation action is a method on the instance:
swiper.slideNext(); // go to next slide
swiper.slidePrev(); // go to previous slide
swiper.slideTo(2); // go to slide at index 2 (0-based)
swiper.slideTo(2, 0); // ...with no animation (speed = 0 ms)
When you use loop: true, prefer slideToLoop(realIndex) to target a slide by its real (original) index rather than its shifted loop index.
3.6 React to slide changes
Register event handlers either inline via the on option or with .on() after init:
// Inline
const swiper = new Swiper('.swiper', {
on: {
slideChange() {
console.log('active slide is now', this.activeIndex);
},
},
});
// Or after the fact
swiper.on('slideChange', () => {
console.log('active index:', swiper.activeIndex);
});
The most useful everyday events:
| Event | Fires when |
|---|---|
slideChange |
The active slide changes (by any means) |
reachBeginning |
The slider reaches the first slide |
reachEnd |
The slider reaches the last slide |
transitionStart / transitionEnd |
A slide animation starts / finishes |
click / tap |
The user clicks or taps the slider |
3.7 Update after you change the DOM
If you add, remove, or resize slides yourself, tell Swiper to recalculate:
// after modifying slides manually
swiper.update();
Alternatively, enable the mutation observer and Swiper updates automatically:
const swiper = new Swiper('.swiper', {
observer: true, // watch the swiper and its children
observeParents: true, // also watch parent elements (e.g. tabs that hide/show)
});
3.8 Destroy a slider
swiper.destroy(); // remove instance + listeners, keep styles
swiper.destroy(true, true); // also strip Swiper's inline styles/classes
Use this when you tear down a component or re-init with very different options.
4. Configuring Your Slider
Swiper is configured by the options object you pass to the constructor. Below are the parameters you'll use most often. (For the complete parameter reference — every option, type, and default — see the Admin Guide.)
4.1 Core layout & behavior
| Option | Default | Purpose |
|---|---|---|
slidesPerView |
1 |
Slides visible at once (number or 'auto') |
spaceBetween |
0 |
Gap between slides (px) |
direction |
'horizontal' |
'horizontal' or 'vertical' |
slidesPerGroup |
1 |
How many slides advance per swipe/click |
centeredSlides |
false |
Center the active slide |
initialSlide |
0 |
Index of the slide shown on init |
speed |
300 |
Transition duration (ms) |
loop |
false |
Infinite continuous loop |
rewind |
false |
At the end, jump back to start (don't use with loop) |
autoHeight |
false |
Container height adapts to the active slide |
grabCursor |
false |
Show a "grab" cursor on hover (desktop nicety) |
allowTouchMove |
true |
Set false to allow only programmatic navigation |
4.2 Looping
const swiper = new Swiper('.swiper', {
loop: true,
});
Loop mode rearranges slides to create a seamless infinite carousel. Because of how it works, your slide count must satisfy a few rules:
- At least
slidesPerView+slidesPerGroupslides (and one more ifcenteredSlidesis on). - A multiple of
slidesPerGroup— or enableloopAddBlankSlidesto auto-pad. - A multiple of
grid.rowswhen using the Grid module — again,loopAddBlankSlidespads for you.
Warning
If you see a console warning about "wrong number of slides for loop mode," you don't have enough slides for a smooth loop. Add more real slides, reduce
slidesPerView, or setloopAddBlankSlides: true(the default) so Swiper inserts blank padding slides.
Caution
Do not combine
loop: truewithrewind: true. They are two different ways to handle reaching the end and are not designed to be used together.
4.3 Responsive breakpoints
Change parameters at different screen widths. Breakpoint keys are min-widths (mobile-first):
const swiper = new Swiper('.swiper', {
// default (smallest screens)
slidesPerView: 1,
spaceBetween: 10,
breakpoints: {
// >= 640px
640: { slidesPerView: 2, spaceBetween: 20 },
// >= 1024px
1024: { slidesPerView: 4, spaceBetween: 40 },
},
});
Note
Only layout-type parameters can change per breakpoint (
slidesPerView,slidesPerGroup,spaceBetween,grid.rows, and similar). Parameters that change core behavior or require a different DOM — likeloopandeffect— cannot be set insidebreakpoints.
You can also key breakpoints by ratio ('@0.75', '@1.5') and switch the basis from the window to the container with breakpointsBase: 'container'.
5. Feature Modules
Swiper's optional features are modules. Import the module, add it to the modules array (or use the bundle), and configure it through its matching option. Here are the modules you'll use most.
5.1 Navigation (prev / next arrows)
import { Navigation } from 'swiper/modules';
import 'swiper/css/navigation';
const swiper = new Swiper('.swiper', {
modules: [Navigation],
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev',
},
});
<div class="swiper">
<div class="swiper-wrapper">...</div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
Useful navigation options:
| Option | Default | Purpose |
|---|---|---|
nextEl / prevEl |
null |
Selector or element for the buttons |
hideOnClick |
false |
Toggle button visibility on slider click |
disabledClass |
'swiper-button-disabled' |
Class added when a button can't be used |
5.2 Pagination (dots, fraction, or progress bar)
import { Pagination } from 'swiper/modules';
import 'swiper/css/pagination';
const swiper = new Swiper('.swiper', {
modules: [Pagination],
pagination: {
el: '.swiper-pagination',
type: 'bullets', // 'bullets' | 'fraction' | 'progressbar' | 'custom'
clickable: true, // let users click a dot to jump to that slide
},
});
<div class="swiper">
<div class="swiper-wrapper">...</div>
<div class="swiper-pagination"></div>
</div>
Pagination highlights:
| Option | Default | Purpose |
|---|---|---|
type |
'bullets' |
'bullets', 'fraction', 'progressbar', or 'custom' |
clickable |
false |
Click a bullet to navigate (bullets only) |
dynamicBullets |
false |
Show only a few bullets when there are many slides |
renderBullet(index, className) |
— | Fully custom bullet HTML |
renderFraction / renderProgressbar / renderCustom |
— | Custom HTML for the other types |
Example custom fraction text:
pagination: {
el: '.swiper-pagination',
type: 'fraction',
renderFraction(currentClass, totalClass) {
return `<span class="${currentClass}"></span> of <span class="${totalClass}"></span>`;
},
}
5.3 Scrollbar
import { Scrollbar } from 'swiper/modules';
import 'swiper/css/scrollbar';
const swiper = new Swiper('.swiper', {
modules: [Scrollbar],
scrollbar: {
el: '.swiper-scrollbar',
draggable: true, // drag the scrollbar to move the slider
hide: true, // auto-hide after interaction (default true)
},
});
<div class="swiper-scrollbar"></div>
5.4 Autoplay
import { Autoplay } from 'swiper/modules';
const swiper = new Swiper('.swiper', {
modules: [Autoplay],
autoplay: {
delay: 3000, // ms between slides (default 3000)
disableOnInteraction: false, // keep autoplaying after the user swipes
pauseOnMouseEnter: true, // pause while hovered
},
});
Control it at runtime through the autoplay namespace:
swiper.autoplay.start();
swiper.autoplay.stop();
swiper.autoplay.pause();
swiper.autoplay.resume();
console.log(swiper.autoplay.running, swiper.autoplay.timeLeft);
Tip
You can give individual slides a different hold time with a
data-swiper-autoplayattribute (in ms):<div class="swiper-slide" data-swiper-autoplay="5000">.
5.5 Transition effects
Set effect and configure the matching <effect>Effect option. Available effects: 'slide' (default), 'fade', 'cube', 'coverflow', 'flip', 'creative', 'cards'.
import { EffectCoverflow } from 'swiper/modules';
import 'swiper/css/effect-coverflow';
const swiper = new Swiper('.swiper', {
modules: [EffectCoverflow],
effect: 'coverflow',
grabCursor: true,
centeredSlides: true,
slidesPerView: 'auto',
coverflowEffect: {
rotate: 50,
depth: 100,
modifier: 1,
slideShadows: true,
},
});
| Effect | Module to import | CSS to import | Key options |
|---|---|---|---|
| Fade | EffectFade |
swiper/css/effect-fade |
crossFade |
| Cube | EffectCube |
swiper/css/effect-cube |
shadow, slideShadows, shadowOffset, shadowScale |
| Coverflow | EffectCoverflow |
swiper/css/effect-coverflow |
rotate, stretch, depth, scale, modifier, slideShadows |
| Flip | EffectFlip |
swiper/css/effect-flip |
slideShadows, limitRotation |
| Cards | EffectCards |
swiper/css/effect-cards |
rotate, perSlideRotate, perSlideOffset, slideShadows |
| Creative | EffectCreative |
swiper/css/effect-creative |
prev/next transforms, limitProgress, perspective |
Note
Effects other than
'slide'are not allowed insidebreakpoints— pick one effect for the slider's lifetime.
5.6 Keyboard control
import { Keyboard } from 'swiper/modules';
const swiper = new Swiper('.swiper', {
modules: [Keyboard],
keyboard: {
enabled: true,
onlyInViewport: true, // only respond when the slider is on screen
pageUpDown: true, // also react to PageUp/PageDown
},
});
5.7 Mousewheel control
import { Mousewheel } from 'swiper/modules';
const swiper = new Swiper('.swiper', {
modules: [Mousewheel],
mousewheel: {
forceToAxis: true, // only react to wheel motion along the slider's axis
releaseOnEdges: true, // let the page scroll once at the first/last slide
sensitivity: 1,
},
});
5.8 Zoom (pinch / double-tap to zoom images)
import { Zoom } from 'swiper/modules';
import 'swiper/css/zoom';
const swiper = new Swiper('.swiper', {
modules: [Zoom],
zoom: { maxRatio: 3, minRatio: 1 },
});
<div class="swiper-slide">
<div class="swiper-zoom-container">
<img src="photo.jpg" />
</div>
</div>
Drive zoom programmatically through the zoom namespace:
swiper.zoom.in(); // zoom into the active slide's image
swiper.zoom.out();
swiper.zoom.toggle();
5.9 Thumbnails (thumbs gallery)
Wire a small "thumbs" Swiper into a main Swiper so clicking a thumbnail navigates the gallery:
import { Thumbs, Navigation } from 'swiper/modules';
import 'swiper/css/thumbs';
const thumbs = new Swiper('.thumbs-swiper', {
slidesPerView: 4,
spaceBetween: 10,
});
const main = new Swiper('.main-swiper', {
modules: [Thumbs, Navigation],
thumbs: { swiper: thumbs },
});
5.10 Grid (multiple rows)
import { Grid } from 'swiper/modules';
import 'swiper/css/grid';
const swiper = new Swiper('.swiper', {
modules: [Grid],
slidesPerView: 3,
grid: {
rows: 2,
fill: 'row', // 'row' or 'column' (default 'column')
},
});
Note
slidesPerView: 'auto'is not compatible with multi-row grids (grid.rows > 1).
5.11 Virtual slides (large data sets)
When you have hundreds of slides, Virtual keeps only the visible ones in the DOM:
import { Virtual } from 'swiper/modules';
import 'swiper/css/virtual';
const swiper = new Swiper('.swiper', {
modules: [Virtual],
virtual: {
slides: ['Slide 1', 'Slide 2', 'Slide 3', /* ...hundreds more */],
},
});
5.12 Free mode (no snapping)
import { FreeMode } from 'swiper/modules';
import 'swiper/css/free-mode';
const swiper = new Swiper('.swiper', {
modules: [FreeMode],
slidesPerView: 'auto',
freeMode: {
enabled: true,
sticky: false, // set true to snap to slides after a flick
},
});
5.13 Accessibility (A11y)
The A11y module adds ARIA roles, labels, and screen-reader messages. It is enabled by default when imported:
import { A11y } from 'swiper/modules';
import 'swiper/css/a11y';
const swiper = new Swiper('.swiper', {
modules: [A11y],
a11y: {
prevSlideMessage: 'Previous slide',
nextSlideMessage: 'Next slide',
paginationBulletMessage: 'Go to slide {{index}}',
},
});
Tip
Messages support the placeholders
{{index}}and{{slidesLength}}, which Swiper fills in for each slide.
5.14 Other modules at a glance
| Module | Import | What it does |
|---|---|---|
Parallax |
swiper/modules |
Parallax-move elements inside slides as you swipe |
Controller |
swiper/modules |
Make one Swiper drive one or more other Swipers |
HashNavigation |
swiper/modules |
Reflect the active slide in the URL hash |
History |
swiper/modules |
Give each slide its own URL via the History API |
Manipulation |
swiper/modules |
Add/remove slides in core (non-framework) usage |
6. Framework Integration
6.1 React
Import the components from swiper/react and the modules from swiper/modules:
import { Swiper, SwiperSlide } from 'swiper/react';
import { Navigation, Pagination } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
export default function Gallery() {
return (
<Swiper
modules={[Navigation, Pagination]}
slidesPerView={3}
spaceBetween={10}
navigation
pagination={{ clickable: true }}
onSwiper={(swiper) => console.log('ready', swiper)}
onSlideChange={(swiper) => console.log('slide', swiper.activeIndex)}
>
<SwiperSlide>Slide 1</SwiperSlide>
<SwiperSlide>Slide 2</SwiperSlide>
<SwiperSlide>Slide 3</SwiperSlide>
</Swiper>
);
}
- Pass every Swiper parameter as a prop (
slidesPerView,spaceBetween,navigation, …). - Listen for events with
on<EventName>props (onSwiper,onSlideChange,onReachEnd, …). - Inside a custom slide component, use the
useSwiper()anduseSwiperSlide()hooks to read the instance and per-slide state (isActive,isVisible,isPrev,isNext).
<SwiperSlide> accepts tag, zoom, lazy, and virtualIndex props, and its children can be a render function receiving { isActive, isVisible, isFullyVisible, isPrev, isNext }.
6.2 Vue
<template>
<swiper
:modules="modules"
:slides-per-view="3"
:space-between="50"
navigation
:pagination="{ clickable: true }"
@swiper="onSwiper"
@slideChange="onSlideChange"
>
<swiper-slide>Slide 1</swiper-slide>
<swiper-slide>Slide 2</swiper-slide>
<swiper-slide>Slide 3</swiper-slide>
</swiper>
</template>
<script>
import { Swiper, SwiperSlide } from 'swiper/vue';
import { Navigation, Pagination } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
export default {
components: { Swiper, SwiperSlide },
setup() {
const onSwiper = (swiper) => { /* save instance */ };
const onSlideChange = () => { /* react */ };
return { onSwiper, onSlideChange, modules: [Navigation, Pagination] };
},
};
</script>
Vue also exposes useSwiper() and useSwiperSlide() composables (returning reactive refs) for use inside child components.
6.3 Web component (<swiper-container>)
Swiper Element is a framework-free custom element. Register it once, then use HTML tags with kebab-case attributes:
<script type="module">
import { register } from 'swiper/element/bundle';
register();
</script>
<swiper-container slides-per-view="3" navigation="true" pagination="true">
<swiper-slide>Slide 1</swiper-slide>
<swiper-slide>Slide 2</swiper-slide>
<swiper-slide>Slide 3</swiper-slide>
</swiper-container>
- Each
SwiperOptionsparameter becomes a kebab-case attribute (slidesPerView→slides-per-view). - The live instance is on the element:
document.querySelector('swiper-container').swiper. - Because the element uses the Shadow DOM, inject custom styles with the
injectStyles/injectStylesUrlsproperties, and namespace DOM events witheventsPrefix.
Note
The web component registers two custom elements:
<swiper-container>and<swiper-slide>. Use theelement/bundleentry for the all-modules build, orswiper/elementif you wire modules yourself.
7. Common Tasks (Recipes)
7.1 A simple image carousel with arrows and dots
import Swiper from 'swiper';
import { Navigation, Pagination } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/pagination';
const swiper = new Swiper('.swiper', {
modules: [Navigation, Pagination],
loop: true,
navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
pagination: { el: '.swiper-pagination', clickable: true },
});
7.2 An auto-advancing hero banner that pauses on hover
import Swiper from 'swiper';
import { Autoplay, Pagination } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/pagination';
const swiper = new Swiper('.hero', {
modules: [Autoplay, Pagination],
loop: true,
autoplay: { delay: 5000, pauseOnMouseEnter: true, disableOnInteraction: false },
pagination: { el: '.swiper-pagination' },
});
7.3 A responsive product grid
const swiper = new Swiper('.products', {
slidesPerView: 1.2,
spaceBetween: 12,
breakpoints: {
640: { slidesPerView: 2.2, spaceBetween: 16 },
1024: { slidesPerView: 4, spaceBetween: 24 },
},
});
7.4 A thumbnail gallery (main + thumbs)
import Swiper from 'swiper';
import { Thumbs, Navigation } from 'swiper/modules';
import 'swiper/css';
import 'swiper/css/navigation';
import 'swiper/css/thumbs';
const thumbs = new Swiper('.thumbs', { slidesPerView: 5, spaceBetween: 8, watchSlidesProgress: true });
const main = new Swiper('.main', {
modules: [Thumbs, Navigation],
navigation: { nextEl: '.swiper-button-next', prevEl: '.swiper-button-prev' },
thumbs: { swiper: thumbs },
});
7.5 Add and remove slides at runtime (core usage)
With the Manipulation module imported, the instance gains slide-editing methods:
swiper.appendSlide('<div class="swiper-slide">New last</div>');
swiper.prependSlide('<div class="swiper-slide">New first</div>');
swiper.addSlide(2, '<div class="swiper-slide">Inserted at index 2</div>');
swiper.removeSlide(0); // remove the first slide
swiper.removeSlide([0, 1]); // remove the first two
swiper.removeAllSlides();
Note
These manipulation methods are for core (vanilla JS) usage. In React and Vue you change your slide data/array instead, and the component re-renders.
8. Best Practices
- [ ] Import only the modules you use. Core + per-module imports keep your bundle small; the bundle build is for prototypes.
- [ ] Always import
swiper/cssplus oneswiper/css/<module>per styled module. - [ ] Give
.swiperan explicit size (or useautoHeight). - [ ] Keep the
swiper→swiper-wrapper→swiper-slidestructure unless you have a strong reason to rename classes. - [ ] Call
swiper.update()(or enableobserver) after you mutate slides outside Swiper. - [ ] Enable A11y for any user-facing slider; add meaningful
prevSlideMessage/nextSlideMessage. - [ ] Mind loop requirements — have enough slides, and keep the count a multiple of
slidesPerGroup/grid.rows. - [ ] Destroy instances when components unmount to avoid leaks (React/Vue wrappers do this for you).
9. How It Works (Concepts)
You don't need this section to use Swiper, but it helps explain the behavior you'll see.
Slides, wrapper, container. The outer .swiper clips the view. The inner .swiper-wrapper is the strip that actually moves (via CSS transform). Each .swiper-slide is one item. Swiper measures these and computes a grid of snap positions.
Active index vs. real index. swiper.activeIndex is the index in the current DOM order. In loop mode Swiper clones/rearranges slides, so the active index is shifted; swiper.realIndex gives you the original slide's index. Target real slides with slideToLoop().
Progress. swiper.progress runs from 0 (first slide / start) to 1 (last slide / end). The progress event reports it continuously — handy for syncing external animations.
Modules. Core Swiper handles layout, touch, and transitions. Everything else — navigation, pagination, autoplay, effects, zoom — is a module that hooks into Swiper's lifecycle via events. That's why the library is tree-shakeable: unused modules are never imported.
Breakpoints. On resize, Swiper recomputes which breakpoint applies and merges its parameters over the defaults. Only layout-safe parameters can change this way.
10. Troubleshooting
Error: console warning Swiper Loop Warning: The number of slides is not enough for loop mode
Cause: Loop mode needs enough slides to create a seamless wrap, and (for grouped/grid layouts) a slide count that is a multiple of slidesPerGroup / grid.rows.
Solution: Add more real slides, lower slidesPerView/slidesPerGroup, or keep loopAddBlankSlides: true (the default) so Swiper pads with blank slides.
Prevention: Plan slide counts as a multiple of your group/row size when designing a looped carousel.
Problem: the slider is invisible or has zero height
Cause: .swiper has no height; the wrapper collapses.
Solution: Set height (and usually width) on .swiper in CSS, or use autoHeight: true.
Prevention: Always style the container before debugging behavior.
Problem: navigation arrows or pagination don't appear
Cause: The module wasn't imported/added to modules, its CSS wasn't imported, or the target element/selector is missing.
Solution: Import the module from swiper/modules, add it to the modules array (or use the bundle), import swiper/css/navigation / swiper/css/pagination, and make sure the nextEl/prevEl/el elements exist.
Prevention: Treat each module as three pieces: JS import + modules entry + CSS import.
Problem: styles look broken / unstyled slides
Cause: Missing core or module CSS imports.
Solution: Import swiper/css always, then swiper/css/<module> for each styled module — or import swiper/css/bundle for everything.
Prevention: Add the CSS import next to every module import.
Problem: slides added via JavaScript don't show up
Cause: Swiper hasn't recalculated after the DOM changed.
Solution: Call swiper.update(), or use the Manipulation methods (appendSlide, etc.) which update for you, or enable observer: true.
Prevention: Enable the observer for sliders whose contents change dynamically.
Problem: vertical slider doesn't scroll
Cause: No height for the vertical track.
Solution: Give .swiper an explicit height so there is room to move vertically.
Prevention: Pair direction: 'vertical' with a sized container.
Problem: the wrong slide is "active" in loop mode
Cause: You're reading activeIndex, which is shifted by loop clones.
Solution: Use swiper.realIndex for the original slide index, and slideToLoop(index) to navigate by real index.
Prevention: In loop mode, always think in terms of realIndex.
Problem: clicking links inside slides triggers a swipe / doesn't open
Cause: Swiper prevents accidental clicks during drags via preventClicks/preventClicksPropagation.
Solution: These are true by default to avoid accidental navigation; if you intentionally need raw clicks, evaluate slideToClickedSlide and the preventClicks* options for your case.
Prevention: Test interactive content inside slides on touch devices early.
Problem: a slider initialized while hidden has wrong sizes
Cause: Swiper measured a display:none container as zero.
Solution: Init after the container is visible, call swiper.update() when it becomes visible, or use observeParents: true to catch show/hide.
Prevention: For tabs/accordions, enable observer + observeParents.
11. FAQ
Q: Do I need jQuery or any other library? No. Swiper is fully standalone and library-agnostic.
Q: Does Swiper include TypeScript types?
Yes — types ship inside the swiper package. There's no separate types package to install.
Q: How do I keep my bundle small?
Import Swiper from swiper, import only the modules you use from swiper/modules, add them to the modules array, and import only the matching swiper/css/<module> files. Swiper is tree-shakeable, so unused modules are dropped.
Q: Bundle build vs. core build — which should I use?
Use the bundle (swiper/bundle, swiper/css/bundle) for quick prototypes where size doesn't matter. Use core + modules for production.
Q: Can I use Swiper without a build tool?
Yes — load swiper-bundle.min.css and swiper-bundle.min.js from a CDN and use the global Swiper.
Q: How do I make a slider that advances by itself?
Import the Autoplay module and set autoplay: { delay: 3000 }.
Q: What's the difference between loop and rewind?
loop creates a seamless infinite carousel by rearranging slides. rewind simply jumps from the last slide back to the first (and vice versa) without cloning. Don't use both together.
Q: How do I change settings on mobile vs. desktop?
Use the breakpoints option (keyed by min-width). Note that only layout parameters can change per breakpoint — not loop or effect.
Q: How do I control the slider from a button outside it?
Keep a reference to the instance and call swiper.slideNext(), swiper.slidePrev(), or swiper.slideTo(index).
Q: How do I get the currently active slide index?
Read swiper.activeIndex (or swiper.realIndex in loop mode).
Q: Can one Swiper control another?
Yes — import the Controller module and set controller: { control: otherSwiper }.
Q: How do I make image slides zoomable?
Import the Zoom module, set zoom: true (or an options object), and wrap each image in <div class="swiper-zoom-container">.
Q: Is Swiper accessible?
Yes — import the A11y module (enabled by default) to add ARIA roles, labels, and screen-reader messages. Provide meaningful message strings.
Q: How do I handle hundreds of slides without slowing the page?
Use the Virtual module so only the slides near the viewport are kept in the DOM.
Q: How do I clean up a Swiper instance?
Call swiper.destroy(). The React and Vue wrappers do this automatically on unmount.
Q: Where is the official site and full API? The project's homepage is swiperjs.com.