Patterns Easy
Lazy Load
Image lazy-loading pattern with placeholders and progressive reveal using IntersectionObserver.
Open in Lab
MCP
vanilla-js css
Targets: JS HTML
Code
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: "Sora", system-ui, sans-serif;
background: #080d18;
color: #e2e8f0;
}
.shell {
width: min(960px, calc(100% - 2rem));
margin: 2rem auto;
}
p {
color: #94a3b8;
}
.gallery {
display: grid;
gap: 1rem;
}
figure {
margin: 0;
min-height: 280px;
border-radius: 16px;
overflow: hidden;
border: 1px solid rgba(255, 255, 255, 0.14);
background: linear-gradient(120deg, #1d2638, #101828);
position: relative;
}
figure::after {
content: "Loading";
position: absolute;
right: 0.8rem;
top: 0.8rem;
font-size: 0.75rem;
color: #cbd5e1;
background: rgba(15, 23, 42, 0.72);
border-radius: 999px;
padding: 0.15rem 0.45rem;
}
figure.loaded::after {
display: none;
}
img {
width: 100%;
height: auto;
display: block;
opacity: 0;
filter: blur(16px);
transform: scale(1.03);
transition: opacity 0.35s ease, filter 0.35s ease, transform 0.35s ease;
}
img.loaded {
opacity: 1;
filter: blur(0);
transform: scale(1);
}(() => {
const images = Array.from(document.querySelectorAll("img[data-src]"));
const loadImage = (img) => {
const src = img.getAttribute("data-src");
if (!src) return;
img.src = src;
img.addEventListener(
"load",
() => {
img.classList.add("loaded");
img.parentElement?.classList.add("loaded");
},
{ once: true }
);
img.removeAttribute("data-src");
};
if (!("IntersectionObserver" in window)) {
images.forEach(loadImage);
return;
}
const observer = new IntersectionObserver(
(entries) => {
for (const entry of entries) {
if (!entry.isIntersecting) continue;
const img = entry.target;
if (img instanceof HTMLImageElement) {
loadImage(img);
observer.unobserve(img);
}
}
},
{ rootMargin: "120px" }
);
for (const img of images) {
observer.observe(img);
}
})();<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
<title>Lazy Load</title>
<link rel="stylesheet" href="style.css" />
</head>
<body>
<main class="shell">
<h1>Lazy Load Images</h1>
<p>Images load only when entering the viewport.</p>
<section class="gallery" id="gallery">
<figure>
<img data-src="https://picsum.photos/seed/phase14-a/900/520" alt="Landscape 1" width="900" height="520" />
</figure>
<figure>
<img data-src="https://picsum.photos/seed/phase14-b/900/520" alt="Landscape 2" width="900" height="520" />
</figure>
<figure>
<img data-src="https://picsum.photos/seed/phase14-c/900/520" alt="Landscape 3" width="900" height="520" />
</figure>
<figure>
<img data-src="https://picsum.photos/seed/phase14-d/900/520" alt="Landscape 4" width="900" height="520" />
</figure>
<figure>
<img data-src="https://picsum.photos/seed/phase14-e/900/520" alt="Landscape 5" width="900" height="520" />
</figure>
</section>
</main>
<script src="script.js"></script>
</body>
</html>Lazy Load
A practical media performance pattern for content-heavy pages.
Features
- Deferred image loading
- Blur placeholder state
- Fade-in reveal when loaded
- Fallback for non-supporting environments