Using Swiper Virtual Slides to Render Hundreds of Items Without Jank
Learn how Swiper’s virtual slides mode keeps DOM nodes constant while rendering large slide sets, with a worked example, setup steps, and verification tips.
28 Jul 2025, 03:49 UTC

Problem: Large slide sets cause layout thrashing
When you need to display a list of hundreds of images, cards, or any repetitive content with Swiper, initializing each slide as a real DOM node forces the browser to layout, paint, and composite every element on every scroll. On mobile devices this quickly drops frame rates and makes the UI feel unresponsive.
Thesis: Enable Swiper’s virtual slides mode to keep only a small, fixed number of DOM nodes in the viewport while preserving the full data set in memory.
How virtual slides work
Swiper’s virtual module renders a limited pool of slide elements (typically the number visible plus a configurable buffer). The actual slide data lives in a JavaScript array; Swiper maps the visible indices to the pooled DOM nodes and updates their content as you scroll. This eliminates most reflow work because the DOM size stays constant.
Setup steps
- Include Swiper from a CDN (or your bundle) and make sure the
virtualmodule is imported. - Prepare a plain array of slide objects – each object can contain any data you need for rendering.
- Initialize Swiper with
virtual: trueand pass the array viavirtual.slides. - Whenever the underlying data changes, call
swiper.virtual.update()(or re‑initialize) so Swimer can remap indices.
Worked example: 500 image cards
<!-- 1. Load Swiper CSS & JS (virtual module included) -->
<link rel="stylesheet" href="https://unpkg.com/swiper/swiper-bundle.min.css" />
<script src="https://unpkg.com/swiper/swiper-bundle.min.js">
<!-- 2. Container for the slider -->
<div class="swiper my-swiper">
<div class="swiper-wrapper">
</div>
<!-- Optional navigation -->
<div class="swiper-button-next"></div>
<div class="swiper-button-prev"></div>
</div>
<script>
// 3. Prepare data (could come from an API)
const totalSlides = 500;
const slidesArray = Array.from({ length: totalSlides }, (_, i) => ({
id: i,
src: `https://picsum.photos/id/${i + 10}/400/300`,
title: `Image ${i + 1}`
}));
// 4. Initialize Swiper with virtual mode
const swiper = new Swiper('.my-swiper', {
modules: [Navigation, Virtual], // ensure Virtual is imported
virtual: {
slides: slidesArray, // the full data set
renderExternal: false, // let Swiper handle rendering
renderSlide: (slide, index) => {
// custom slide template
return `
<div class="swiper-slide">
<img src="${slide.src}" alt="${slide.title}" />
<p>${slide.title}</p>
</div>
`;
}
},
navigation: {
nextEl: '.swiper-button-next',
prevEl: '.swiper-button-prev'
},
// optional: limit how many extra slides are kept in the buffer
virtual: { ...swiper.params.virtual, slidesPerView: 'auto', addSlidesBefore: 1, addSlidesAfter: 1 }
});
// 5. Example: updating data after an async fetch
async function refreshData() {
const newData = await fetch('/api/new-images').then(r => r.json());
// replace the internal array
swiper.virtual.slides = newData;
// tell Swiper to rebuild the internal map
swiper.virtual.update();
}
</script>
Trade‑offs and limitations
- Effects that require simultaneous rendering of multiple slides (e.g.,
coverflow,cube,fadewith cross‑slide interaction) are not compatible with virtual mode because they depend on having all slides in the DOM at once. - If you manipulate slide elements directly with
querySelectoror similar, you must do it inside thevirtual.rendercallback or after callingswiper.virtual.update(); otherwise you’ll work with stale or duplicated nodes. - Changing the slide count or order without calling
swiper.virtual.update()leads to index mismatches and blank spots.
Verification checklist
- Open the page in Chrome DevTools → Elements tab. Verify that the number of
.swiper-slideelements in the DOM equalsslidesPerView + addSlidesBefore + addSlidesAfter(usually 3‑5) regardless of the total slide count. - Open the Performance tab, record a scroll session, and ensure layout shift events stay low and the FPS graph hovers near 60 fps.
- Check the console for no warnings about “virtual slide index out of range”.
Actionable closing
If your Swiper instance needs to handle more than a few dozen items, turn on virtual slides. It reduces DOM churn, keeps frame rates smooth on mobile, and works seamlessly with navigation, pagination, and lazy‑load. Remember to update the virtual module whenever your data changes, and avoid direct DOM manipulation outside of Swiper’s callbacks. With those guards in place, you can scroll through hundreds of slides without the jank.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.