Diagnosing Blank Slides in Swiper Lazy‑Loading Carousels
A step‑by‑step diagnostic guide for Swiper carousels where lazy‑loaded images stay blank, covering missing attributes, config gaps, hidden containers, network errors, CSS clipping, responsive breakpoints, and multi‑instance conflicts.
06 Aug 2025, 22:02 UTC

Recognizable Condition
One or more slides stay blank while the carousel is interactive. The slide container exists, but the <img> inside never receives a src value, even after the slide scrolls into view.
Cause / Diagnostic Table
| Observed Symptom | Likely Cause | Quick Verification |
|---|---|---|
Image element has no data-src or data-srcset | Missing lazy‑load attributes on the <img> | Inspect the slide in DevTools → Elements; confirm attribute presence. |
| All slides render but never defer loading | Lazy loading not enabled in Swiper options | Log swiper.params.lazy after init; should be truthy. |
| Slides load after a tab becomes visible or a modal opens | Container was display:none or detached at init | Call swiper.update() after reveal; watch for loads. |
| Network panel shows 404/403/CORS for image URLs | Bad URLs, expired signed links, or cross‑origin restrictions | Open Network → filter “Img”; check status codes. |
| Images load but appear invisible | CSS collapses slide height, zeroes image dimensions, or clips with overflow/transform | Computed styles on slide and <img>; look for height:0 or overflow:hidden. |
| Works on desktop, fails on mobile (or only after resize) | Responsive breakpoints or per‑slide options change lazy behavior | Resize viewport; observe when lazy toggles. |
| Multiple carousels on page, only one misbehaves | Shared selector initializes wrong element or leaves one instance without lazy | Give each carousel a unique class/ID; verify each instance’s params.lazy. |
Ordered Checks
- Confirm lazy attributes – In DevTools, select the problematic
<img>. It must havedata-src(ordata-srcset) and the classswiper-lazy(Swiper ≥ 6) orswiper-lazy-preloaderfor older versions. - Verify Swiper config – Run in console (page context, no special permissions):
Expectconst swiper = document.querySelector('.my-swiper').swiper; console.log('lazy enabled:', swiper?.params?.lazy);trueor an object withloadPrevNext,loadOnTransitionStart, etc. Ifundefined, lazy is off. - Check container visibility at init – If the slider lives in a hidden tab, call after the tab shows:
Observe Network for new image requests.swiper.update(); swiper.lazy.load(); // Swiper ≥ 8 - Inspect Network for failures – Filter “Img” while scrolling to the blank slide. 404/403/CORS entries mean the URL is the problem, not Swiper.
- Audit CSS dimensions – In Computed styles, ensure the slide wrapper has a non‑zero height/width and the
<img>isn’t forced todisplay:noneorwidth:0. - Test responsive breakpoints – Resize the viewport across defined breakpoints (e.g., 640 px, 1024 px). Note if lazy toggles on/off. Check
swiper.params.breakpointsfor per‑breakpoint lazy settings. - Validate instance isolation – Ensure each carousel uses a unique selector (e.g.,
.swiper-hero,.swiper-gallery). Re‑init each with its own config object.
Fixes Tied to Findings
- Add missing attributes – Update the template so every lazy image includes
data-src="{{imageUrl}}"and classswiper-lazy. - Enable lazy in config – Minimal init for Swiper 9+:
For Swiper 5/6 usenew Swiper('.my-swiper', { lazy: true, // or { loadPrevNext: true } // other options… });lazyLoading: true. - Re‑initialize after reveal – In the tab‑show handler:
const swiper = document.querySelector('.tab-pane.active .my-swiper').swiper; if (swiper) { swiper.update(); swiper.lazy.load(); } - Correct image URLs – Replace broken URLs, renew signed links, or add proper CORS headers on the image host.
- Fix CSS clipping – Ensure slide containers have explicit dimensions (e.g.,
.swiper-slide { height: auto; }) and avoidoverflow:hiddenon ancestors that hide the image. - Adjust breakpoint config – If lazy should stay on at all sizes, define it once at the root level, not inside a breakpoint object.
breakpoints: { 640: { slidesPerView: 2 }, 1024: { slidesPerView: 4 } }, lazy: true // global - Separate instances – Give each carousel a distinct class and initialise separately:
new Swiper('.swiper-hero', { lazy: true }); new Swiper('.swiper-gallery', { lazy: { loadPrevNext: true } });
Escalation Criteria
Escalate to the Swiper maintainers or a deeper code review when:
- A minimal reproduction (single Swiper, unique selector, known‑good image URLs, visible container) still shows blank slides after all checks above.
- The failure correlates with a specific Swiper release (e.g., works in 9.4.1, breaks in 10.0.0).
- Console shows Swiper‑internal errors (e.g.,
Uncaught TypeError: swiper.lazy.load is not a function) indicating a version mismatch between core and lazy module.
Minimal Reproduction Example
Save the following as test-swiper-lazy.html and open in a browser. It isolates the lazy‑loading path.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Swiper Lazy Test</title>
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.css">
<style>
.swiper-slide img { width: 100%; height: auto; display: block; }
.swiper { width: 600px; height: 300px; margin: 2rem auto; }
</style>
</head>
<body>
<div class="swiper test-swiper">
<div class="swiper-wrapper">
<div class="swiper-slide"><img class="swiper-lazy" data-src="https://picsum.photos/id/101/800/400" alt="Slide 1"></div>
<div class="swiper-slide"><img class="swiper-lazy" data-src="https://picsum.photos/id/102/800/400" alt="Slide 2"></div>
<div class="swiper-slide"><img class="swiper-lazy" data-src="https://picsum.photos/id/103/800/400" alt="Slide 3"></div>
</div>
<div class="swiper-pagination"></div>
</div>
<script src="https://cdn.jsdelivr.net/npm/swiper@11/swiper-bundle.min.js"></script>
<script>
const swiper = new Swiper('.test-swiper', {
lazy: true,
pagination: { el: '.swiper-pagination', clickable: true }
});
// Debug hook
swiper.on('lazyImageReady', (slideEl, imgEl) => {
console.log('Loaded:', imgEl.src);
});
</script>
</body>
</html>
If images appear and the console logs “Loaded: …”, the lazy pipeline works. Compare this baseline against the production page to isolate environmental differences.
Limitations & Verification
- Lazy‑loading option names differ across major versions (e.g.,
lazyLoadingvslazy). Confirm the installed major version (swiper --versionorpackage.json) before copying config snippets. - Framework wrappers (React, Vue, Svelte) may re‑render slides and strip
data-src. Always inspect the live DOM, not the source template. - Ad‑blockers or privacy extensions can block image requests; test in an incognito window to rule them out.
Practical verification: After applying a fix, scroll to the previously blank slide and confirm in DevTools → Network that a request for the image URL fires and returns 200, and that the <img>’s src attribute updates to that URL.
0 replies
A thoughtful contribution can make all the difference. Be the first to share one.