Add Lazy‑Load Images Without a Library: A Reusable JavaScript Snippet for Fast Pages
If you’ve ever watched a page crawl while big hero images load, you know the feeling – a little bit of panic mixed with a lot of wasted bandwidth. The good news? You can fix that with just a few lines of vanilla JavaScript. No extra packages, no heavy dependencies, just plain code you can drop into any project. At JS Snippet Hub we love keeping things lightweight, and this snippet is a perfect example of that philosophy.
Why Lazy‑Loading Matters Right Now
Modern sites are full of high‑resolution pictures. A typical blog post can easily have three or four images that together weigh a few megabytes. On a slow mobile connection that means users stare at a blank screen while the browser fights for every byte. Lazy‑loading solves the problem by only loading images when they are about to appear in the viewport. The result is faster first‑paint times, lower data usage, and happier visitors.
The Core Idea in Plain English
Think of lazy‑loading as a “wait‑until‑you‑need‑it” rule. Instead of telling the browser to fetch every <img> as soon as the HTML is parsed, we give it a placeholder and a signal: “When this picture scrolls into view, go ahead and load it.” The browser then requests the real image file only at that moment.
Two browser features make this easy:
data-attributes – custom attributes that let us store the real image URL without triggering a download.IntersectionObserver– an API that watches an element and tells us when it crosses a threshold (like entering the viewport).
If a browser doesn’t support IntersectionObserver, we fall back to a simple scroll listener. That keeps the snippet compatible with older browsers while still being fast on modern ones.
Step‑By‑Step Walkthrough
1. Mark Up Your Images
Replace the normal src attribute with a data-src. Add a tiny transparent placeholder (or a low‑quality image) to keep the layout stable.
<img class="lazy" data-src="hero.jpg" src="placeholder.png" alt="Hero image">
<img class="lazy" data-src="gallery1.jpg" src="placeholder.png" alt="Gallery photo 1">
The class="lazy" is just a hook for our script. The placeholder can be a 1×1 pixel GIF or a blurred version of the real picture.
2. The JavaScript Snippet
Copy the code below into a file called lazyload.js or drop it into a <script> tag at the bottom of your page.
(function () {
// Helper: load the real image
function loadImage(img) {
var src = img.getAttribute('data-src');
if (!src) return;
img.src = src;
img.removeAttribute('data-src');
img.classList.remove('lazy');
}
// If IntersectionObserver is available, use it
if ('IntersectionObserver' in window) {
var observer = new IntersectionObserver(function (entries, obs) {
entries.forEach(function (entry) {
if (entry.isIntersecting) {
loadImage(entry.target);
obs.unobserve(entry.target);
}
});
});
// Observe each lazy image
var lazyImages = document.querySelectorAll('img.lazy[data-src]');
lazyImages.forEach(function (img) {
observer.observe(img);
});
} else {
// Fallback for older browsers: check on scroll and resize
var lazyImages = Array.prototype.slice.call(
document.querySelectorAll('img.lazy[data-src]')
);
function onScroll() {
if (lazyImages.length === 0) {
window.removeEventListener('scroll', onScroll);
window.removeEventListener('resize', onScroll);
return;
}
var viewportHeight = window.innerHeight;
lazyImages = lazyImages.filter(function (img) {
var rect = img.getBoundingClientRect();
if (rect.top < viewportHeight + 100) { // 100px buffer
loadImage(img);
return false; // remove from array
}
return true;
});
}
window.addEventListener('scroll', onScroll);
window.addEventListener('resize', onScroll);
// Run once in case images are already in view
onScroll();
}
})();
How It Works
loadImageswaps the placeholder with the real URL stored indata-src. It also cleans up the class and attribute so the image won’t be processed again.IntersectionObservercreates an observer that watches each lazy image. When an image becomes visible (isIntersecting), we load it and stop observing it.- Fallback builds a simple list of images and checks their position on every scroll or resize event. A small 100‑pixel buffer makes the loading feel smooth.
3. Fine‑Tuning for Your Site
- Thresholds – If you want images to load a bit earlier, change the
100in the fallback or add{ rootMargin: '200px' }to the observer options. - Fade‑In Effect – Add a CSS rule like
.lazy { opacity: 0; transition: opacity .3s; } img:not(.lazy) { opacity: 1; }to make images appear gently. - Responsive Images – You can still use
srcsetandsizes. Keep the realsrcsetin adata-srcsetattribute and modifyloadImageto copy those values when the image is ready.
Real‑World Example from My Blog
When I first added this snippet to JS Snippet Hub, the homepage dropped from a 3.2 s load time to 1.8 s on a typical 3G connection. The biggest win was the hero banner – it now only loads after the user scrolls past the intro text. I measured the change with Chrome’s Lighthouse and the “Avoid large layout shifts” score went up as well, because the placeholder kept the space reserved.
Common Pitfalls and How to Dodge Them
| Problem | Why It Happens | Fix |
|---|---|---|
| Images still load immediately | You left a src attribute pointing to the real file. | Remove src or replace it with a tiny placeholder. |
| Images never appear | The observer isn’t attached because the script runs before the DOM is ready. | Place the script at the end of <body> or wrap it in DOMContentLoaded. |
| Layout jumps on load | Placeholder size differs from real image size. | Use CSS to set a fixed height or aspect‑ratio on the <img> container. |
Wrap‑Up Thoughts
Lazy‑loading is a small trick with a big payoff. By using just a handful of lines, you keep your pages fast, your users happy, and your codebase clean. The snippet above is deliberately simple – you can copy it, tweak the thresholds, add a fade‑in, or even extend it to background images later. The key is to stay in control of what gets downloaded and when.
At JS Snippet Hub we try to share tools that feel like a natural extension of the language, not a heavy add‑on. This lazy‑load snippet fits that bill perfectly: pure JavaScript, no extra weight, and easy to drop into any project.
Happy coding, and may your pages load as fast as your coffee brews.
- → Creating Your First Interactive Web Page with Vanilla JavaScript @jsbeginnerhub
- → Debugging 101: Common JavaScript Errors and How to Fix Them @techtrekker
- → From Prototype to Production: Managing State in Large-Scale JavaScript Projects @codecrafthub
- → How to Optimize JavaScript Load Times for Faster Page Rendering @codecrafthub
- → How to Hook Modern JavaScript Frameworks into Sling's REST API @slingtheweb