Swiper.js has become a go-to solution for web developers looking to build sleek, responsive sliders that enhance user engagement. One of its most powerful features is the centeredSlides option, which keeps the active slide perfectly centered in view. However, starting with Swiper.js version 11, using centeredSlides—especially in cases with a small number of slides or dynamic content—can introduce some tricky transition issues.
In this guide, we’ll walk through a practical solution: using duplicate slides. By smartly duplicating your original slides, you can eliminate those common transition hiccups and deliver a smoother, more polished user experience. We’ll cover everything from the foundational HTML and CSS to the jQuery and Swiper.js logic that brings it all together. Let’s unlock the full potential of centeredSlides in your Swiper.js sliders!
Setting the Stage: HTML and CSS Essentials
Before jumping into JavaScript, let’s first lay down a solid foundation with our HTML and CSS.
HTML Structure
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
| <div class="slider_center">
<div class="inner">
<ul class="slider_list swiper-wrapper">
<li class="swiper-slide">1</li>
<li class="swiper-slide">2</li>
<li class="swiper-slide">3</li>
<li class="swiper-slide">4</li>
<li class="swiper-slide">5</li>
<li class="swiper-slide">6</li>
<li class="swiper-slide">7</li>
<li class="swiper-slide">8</li>
<li class="swiper-slide">9</li>
<li class="swiper-slide">10</li>
</ul>
</div>
<div class="pagination"></div>
<div class="swiper-button-prev"></div>
<div class="swiper-button-next"></div>
</div>
|
- .slider_center: Acts as the main wrapper, managing overflow and positioning.
- .inner: Helps achieve a centered layout—especially useful when dealing with duplicate slides.
- .slider_list.swiper-wrapper: Holds the slide items, as required by Swiper.js.
- Pagination and navigation buttons: Improve user control and accessibility.
CSS Styling
1
2
3
4
5
6
| .slider_center { overflow: hidden; position: relative; margin-top: 100px; }
.slider_list > li { display: flex; justify-content: center; align-items: center; width: 300px; height: 400px; background: #8ab4f8; font-size: 36px; font-weight: 500; }
.pagination { display: flex; justify-content: center; margin-top: 20px; }
.swiper-button-next, .swiper-button-prev { color: #000; }
/* When using coverflow and slidesPerView: 'auto', side slides may not display properly even with a sufficient number of slides. Adjust the width value (e.g., 200vw) below to ensure correct spacing and visibility. */
.slider_center .inner { position: relative; left: 50%; width: 200vw; transform: translate(-50%); }
|
Why This Layout Works
Let’s break down the .inner styles:
- width: 200vw: Gives enough space for both original and duplicated slides to coexist without layout issues.
- left: 50%: Centers the left edge of the container.
- transform: translate(-50%): Nudges the container back by half its width, perfectly centering the slide strip in the viewport.
This structure isn’t just for looks—it’s vital for how Swiper handles looping and centering. With centeredSlides: true and loop: true, these styles ensure transitions are seamless and the active slide stays perfectly centered throughout.
Optimizing Transitions with jQuery
Now that the layout is ready, let’s add the JavaScript logic to duplicate slides and initialize Swiper for a smooth, centered experience.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
| function centerSlider() {
// Select the original slide items and the list to append cloned slides
const $sliderList = $('.slider_list');
const $originalList = $('.slider_list > li');
const originalSlideCount = $originalList.length;
const targetCount = 20; // Desired total number of slides
// Count current (non-cloned) slides
const currentSlideCount = $sliderList.find('> li[data-cloned!=true]').length;
// Calculate how many clones are needed to meet the target
const neededClones = Math.max(0, Math.ceil((targetCount - currentSlideCount) / originalSlideCount));
// Clone and append the necessary number of original slides
for (let i = 0; i < neededClones; i++) {
$originalList.each(function () {
const $clone = $(this).clone();
$sliderList.append($clone);
});
}
// Initialize Swiper with desired settings
const swiperOptions = {
loop: true,
centeredSlides: true,
slidesPerView: 'auto',
autoplay: {
delay: 2000,
disableOnInteraction: false,
},
effect: 'coverflow',
coverflowEffect: {
rotate: 0,
stretch: -50,
depth: 200,
modifier: 1,
slideShadows: true,
},
pagination: {
el: '.slider_center .pagination',
type: 'bullets',
clickable: true,
renderBullet: (index, className) => index < originalSlideCount ? `<span class="${className}"></span>` : '',
},
navigation: {
nextEl: '.slider_center .swiper-button-next',
prevEl: '.slider_center .swiper-button-prev',
},
};
// Create Swiper instance
const swiper = new Swiper('.slider_center .inner', swiperOptions);
// Update pagination bullets on slide transition start
swiper.on('transitionStart', () => {
const currentIndex =
swiper.realIndex < originalSlideCount
? swiper.realIndex
: swiper.realIndex % originalSlideCount;
const $paginationItems = $('.slider_center .pagination .swiper-pagination-bullet');
$paginationItems.removeClass('swiper-pagination-bullet-active');
$paginationItems.eq(currentIndex).addClass('swiper-pagination-bullet-active');
});
// Handle autoplay behavior after looped slides transition
let autoplayActive = false;
swiper.on('transitionEnd', () => {
const currentIndex = swiper.realIndex;
if (currentIndex >= originalSlideCount) {
const originalIndex = currentIndex % originalSlideCount;
swiper.slideToLoop(originalIndex, 0); // Instantly jump to the original slide
autoplayActive = true;
}
if (autoplayActive) {
swiper.autoplay.start(); // Restart autoplay
setTimeout(() => {
autoplayActive = false;
}, 1000); // Reset flag after a short delay
}
});
}
|
How It Works
- Element Selection: We grab all the original slides and count them.
- Cloning Logic: If there aren’t enough slides to make loop and centeredSlides work smoothly, we clone the originals until we hit our desired count.
- Swiper Setup: We configure Swiper with looping, centering, autoplay, navigation, and a custom pagination renderer.
- Pagination Sync: We listen for transition events to keep pagination accurate, even with duplicated slides.
Why Cloning Is Important
When using both loop: true and centeredSlides: true, Swiper internally duplicates slides to create a seamless loop. However, if your original slide count is too low, Swiper might not have enough material to work with—leading to stutters, jumps, or off-center transitions.
By manually cloning slides, we ensure:
- Smooth looping without visual glitches
- Consistent centering of the active slide
- An overall better user experience
Final Tips
- Don’t overdo the cloning—too many slides can hurt performance.
- Keep your target slide count reasonable for best results.
- Test your slider across screen sizes to ensure responsive behavior.
Conclusion
In this tutorial, we tackled a common issue with Swiper.js’s centeredSlides feature and shared a smart fix using duplicate slides. By applying these techniques, you can create sliders that look better, feel smoother, and behave more consistently—even with limited or dynamic content.
Armed with these tips, you’re ready to elevate your Swiper.js sliders and deliver a refined user experience that stands out. Happy coding!