Embla Carousel Lazy-Loaded Images with slidesInView — Free Snippet

Embla Carousel Lazy-Loaded Images with slidesInView · Carousels · Plain HTML, CSS & JS · Live preview

What's included

Features

Visibility-driven loading
Embla's slidesInView event and method.
Early trigger
inViewThreshold: 0 counts peeking slides.
One-slide look-ahead
The next slide is fetched in advance.
Shimmer placeholders
Shown until the image loads.
Fade-in reveal
Opacity transition on load.
Error state
A message replaces failed images.
No layout shift
Fixed aspect-ratio frames.
Download counter
Live count plus a visual map.

About this UI Snippet

Embla Carousel Lazy Loading — Download Only What People Actually See

Screenshot of the Embla Carousel Lazy-Loaded Images with slidesInView snippet rendered live

A carousel of 18 photos that loads everything up front makes visitors download images most of them will never swipe to. Lazy loading downloads a photo only when its slide is about to be seen. Embla reports which slides are visible, which makes this straightforward and more precise than native loading="lazy".

Why not loading="lazy"?

Native lazy loading works on distance from the viewport, but slides inside an overflow: hidden carousel are positioned with transforms. Browsers handle this inconsistently: some load hidden slides immediately, others don't load them until too late. Asking the carousel itself is reliable.

slidesInView drives the loading

Each image starts with its URL in data-src and no src, so nothing is requested. Embla's slidesInView event fires whenever the set of visible slides changes, and embla.slidesInView() returns their indexes. The handler copies data-src into src for those slides. With inViewThreshold: 0, a slide counts as visible as soon as one pixel shows, so a peeking slide starts loading early.

Look one slide ahead

Loading only what's visible means the next swipe often lands on a placeholder. The handler also loads the slide after each visible one, which hides most of the latency without downloading the whole set.

Placeholders, fade-in and errors

A shimmer placeholder covers each frame until the image's load event, then the image fades in. The error event swaps in a message instead of leaving an endless shimmer. The frame has a fixed aspect ratio, so nothing jumps when images arrive.

Seeing the effect

The strip of squares under the carousel shows which photos have downloaded and which are in view, and the header counts downloads, so you can watch requests happen as you swipe.

Build with AI

Build, Understand, Optimize, and Extend It With AI

Hand this snippet to an AI assistant like Claude and ask it why slidesInView is more reliable than native lazy loading inside a carousel. Ask it to add responsive srcset and sizes, low-quality blurred placeholders, preloading two slides ahead on fast connections using the Network Information API, or retrying failed images. It can also measure the effect on page weight with your own images.

Prompt to recreate it

Copy this into your AI assistant of choice to build the effect from scratch, or as a jumping-off point for your own variant:

text
Build a lazy-loading image carousel with Embla Carousel v8 (loaded from a CDN as UMD) in plain HTML, CSS and JavaScript.

Requirements:
- Eighteen photo slides, two per view on phones and three on wider screens, aligned to the start with trimmed ends.
- Each image starts with its URL in data-src and no src, inside a frame with a fixed 4:5 aspect ratio and a shimmer placeholder.
- Treat a slide as in view as soon as one pixel is visible, and on the slidesInView event load the visible slides plus the next one.
- Fade images in on load, show an error message if an image fails, and never load an image twice.
- Show a live "X of 18 photos downloaded" counter and a row of small squares indicating which photos are downloaded and which are in view.
- Add previous and next buttons that disable at the ends, and respect reduced motion for the shimmer and fade.

Want to tighten it up first? Run this prompt through the AI Prompt Studio to score it across 8 quality dimensions, catch anti-patterns, and tune the wording for Claude, ChatGPT, or Gemini before you paste it in.

Source Code

<div class="ell">
  <div class="ell-head">
    <h2>Photo roll</h2>
    <div class="ell-stat" id="ellStat" aria-live="polite"></div>
  </div>
  <div class="ell-viewport" id="ellViewport">
    <div class="ell-container" id="ellList"></div>
  </div>
  <div class="ell-foot">
    <button type="button" id="ellPrev" aria-label="Previous photos">‹</button>
    <div class="ell-dots" id="ellMap" aria-hidden="true"></div>
    <button type="button" id="ellNext" aria-label="Next photos">›</button>
  </div>
</div>

Step by step

How to Use

  1. 1
    Load EmblaInclude embla-carousel.umd.js from the CDN.
  2. 2
    Paste the snippetOnly the first few photos download; the rest show shimmer placeholders.
  3. 3
    Swipe or use the arrowsPhotos load as their slides approach the viewport.
  4. 4
    Watch the mapBlue squares are downloaded; outlined squares are in view.
  5. 5
    Use your imagesPut real URLs in data-src and keep src empty.

Real-world uses

Common Use Cases

Product and photo galleries
Large sets without heavy page weight.
Mobile-first sites
Save data on slow connections.
Real-estate listings
Dozens of photos per property.
Social feeds
Horizontal media rows.
Performance work
Improve Largest Contentful Paint.
Related: Lazy-Loaded Carousel
A dependency-free take: Lazy-Loaded Carousel.
Related: Lazy Load Image Grid
IntersectionObserver in a grid: Lazy Load Image Grid.

Got questions?

Frequently Asked Questions

Store each image URL in a data attribute, listen for the slidesInView event, and for each index returned by embla.slidesInView() copy the data attribute into src. Mark loaded slides so they are only processed once.

Carousel slides are hidden with overflow and moved with transforms, and browsers treat them inconsistently for native lazy loading. Using the carousel's own visibility information is more predictable.

It sets how much of a slide must be visible to count as in view, from 0 to 1. A value of 0 means a single visible pixel is enough, which starts loading as soon as a slide peeks in.

Usually yes. Loading one slide beyond the visible ones hides most network latency on the next swipe, while still avoiding downloading the whole gallery.

Give each frame a fixed aspect ratio, or width and height attributes on the image, so space is reserved before the image arrives.