You have about three seconds.
That's roughly how long you have before a visitor decides whether your website is worth their time. If your landing page takes longer than that to become interactive, you've already lost them. They'll click the back button, and you'll never know what they could have become—a user, a customer, an advocate for your product.
When I built the Jottings landing page, I was obsessed with that three-second window. Not because I'm a performance perfectionist (though I might be), but because slow websites are objectively worse websites. They frustrate users, they perform poorly in search results, and they convert worse. So I started looking for ways to squeeze every millisecond out of the initial load.
That's where lazy loading came in.
What Is Lazy Loading?
Lazy loading is a simple idea with powerful results: don't load everything at once. Instead, load what the user can see right now, and defer everything else until they actually need it.
Think of it like a bookstore. You don't need to print every book in your catalog when someone walks through the door. You just show them the front display. When they want to browse a specific section, that's when you pull out the relevant shelves.
On a webpage, lazy loading typically applies to images, video, and heavy interactive components. Instead of loading every image on the page when the HTML arrives, you load just the image that's visible in the viewport. When a user scrolls down and another image comes into view, that's when you fetch it.
The impact is immediate: your initial page load is faster, your Largest Contentful Paint (LCP) metric improves, and users see an interactive page sooner.
The Modern Way: IntersectionObserver
For years, developers detected when elements came into view using scroll event listeners. You'd calculate the position of every element on the page and compare it to the viewport. It was CPU-intensive and buggy.
Then IntersectionObserver arrived, and everything changed.
IntersectionObserver is a browser API that watches elements and tells you when they enter or leave the viewport. It's efficient, built-in, and handles all the math for you. Better yet, it respects your user's device limitations—it runs asynchronously and doesn't block the main thread.
Here's the basic pattern:
const observer = new IntersectionObserver((entries) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
// Element is visible! Load it now
loadContent(entry.target);
// Stop watching this element
observer.unobserve(entry.target);
}
});
});
// Watch all lazy-loadable elements
document.querySelectorAll('[data-lazy]').forEach(el => {
observer.observe(el);
});
That's it. The browser handles the heavy lifting. No scroll listeners fighting for CPU time. No janky performance. Just clean, efficient loading.
How Jottings Uses Lazy Loading
On the Jottings landing page, I've applied lazy loading to everything that makes sense:
Hero images and screenshots load immediately (they're above the fold), but the decorative images further down the page wait until you scroll to them. This means the critical content renders faster, your LCP metric stays green, and users on slow connections aren't forced to download images they might never see.
Feature showcase images are a perfect candidate for lazy loading. The landing page has five or six sections describing different features. Each one has an accompanying image. Instead of loading all six images upfront, I only load the ones that are visible. When you scroll down, the next image loads smoothly in the background.
Demo video embeds use a clever trick: instead of loading the actual video player on page load (which is heavy), I show a static preview image. When you scroll to it or click play, the actual video player loads. This cuts initial payload by a lot.
Heavy interactive components that live below the fold are deferred entirely. This might be an animated chart, an interactive calculator, or anything that requires JavaScript. The code loads lazily too—we only parse and execute it when the component becomes visible.
The result? My first contentful paint dropped from 2.8 seconds to 1.2 seconds. Largest contentful paint improved from 3.4 seconds to 1.8 seconds. That's the difference between a user staying and a user bouncing.
Why This Matters: Core Web Vitals
Google's Core Web Vitals are three metrics that measure user experience:
- Largest Contentful Paint (LCP): How long until the largest visible element loads. Target: under 2.5 seconds.
- First Input Delay (FID): How responsive the page is to user input. Target: under 100ms.
- Cumulative Layout Shift (CLS): How much the page jumps around while loading. Target: under 0.1.
Lazy loading directly improves LCP. By deferring non-critical content, the important stuff loads first. Users see a fully interactive page faster, which feels like a faster site. And it is—where it matters most.
The secondary benefit is FID. When you're not loading a hundred images on page load, you're not making the browser work as hard. IntersectionObserver runs asynchronously, so it doesn't block the main thread. Your site stays responsive while lazy content loads in the background.
And honestly? Google rewards fast sites. Better Core Web Vitals means better search rankings. Better search rankings means more traffic. It's a virtuous cycle.
The Implementation Details
On the Jottings landing page, the lazy loading is baked into the custom build system. When the site builds, images that should be lazy-loaded get a special attribute: data-lazy="true". The JavaScript then picks up on that attribute and starts observing.
I also use placeholder images or low-quality image previews (LQIP) for above-the-fold content, so users see something while the full image loads. It's a small detail, but it makes the experience feel more polished.
The whole system integrates with the HTML minification and CSS optimization that's part of the Jottings build pipeline. The lazy loading logic is tiny—maybe 2KB gzipped—so adding it has almost zero cost.
The Bigger Picture
Lazy loading is one piece of the performance puzzle. You also want to minify your HTML and CSS, optimize your JavaScript, use a CDN (Jottings uses Cloudflare Pages), and monitor your metrics regularly.
But it's a foundational piece. It's one of the highest-impact changes you can make with minimal complexity. It's also a gift to users on slow connections or mobile devices—and that's a growing majority of the internet.
If you're building a landing page or any public-facing website, lazy loading should be on your to-do list. Your users will thank you, even if they don't know why your site feels snappier than the rest.
And if you're tired of wrestling with landing page performance, that's exactly the problem Jottings solves. Build fast, static sites with built-in performance optimization. No build system complexity. No performance anxiety.
Want to see lazy loading in action? Create a Jottings site and start publishing. Your content deserves a fast home.