Lazy Loading, Defer, and Async: Loading Your Site the Smart Way
Every element on your website (every image, script, font, and stylesheet) is a request your visitor’s browser has to make. The more requests, the longer the page takes to load. The longer the page takes to load, the more visitors leave before they ever see your content.
But here’s the thing most business owners don’t realize: it’s not just about reducing the total number of requests. It’s about controlling when and how those resources load. A 3MB hero image that loads instantly matters more than a tracking script buried in the footer. A chat widget that loads after the main content is visible creates a completely different experience than one that blocks the entire page from rendering. If you would rather see how it all fits together, we wrote a deeper look at what Core Web Vitals actually measure.
That’s where lazy loading, defer, and async come in. These aren’t obscure developer tricks. They’re fundamental techniques that determine whether your website feels fast or sluggish. We use all three on every site we build, and understanding them will help you make smarter decisions about your website’s performance.
The Problem: Everything Loads at Once
By default, when a browser loads a web page, it tries to download and process everything in order, from top to bottom. It reads your HTML, and every time it encounters an image, a stylesheet, or a script, it stops what it’s doing, fetches that resource, processes it, and then continues.
This means a JavaScript file in your page’s <head> section can delay your entire page from rendering, even if that script is for a chat widget the user won’t interact with for 30 seconds. An image at the bottom of a long page gets downloaded at the same time as the hero image at the top, competing for bandwidth.
The result: your visitor stares at a blank or partially loaded screen while the browser fetches resources they don’t even need yet.
Lazy Loading: Don’t Load It Until They Need It
Lazy loading is exactly what it sounds like: delaying the loading of a resource until it’s actually needed. The most common application is images, but it applies to videos, iframes, and even entire sections of JavaScript.
How It Works for Images
Without lazy loading, a page with 20 images downloads all 20 images immediately, even if only 3 are visible on screen. With lazy loading, images below the fold don’t start downloading until the user scrolls close to them.
Modern browsers support this natively with a single HTML attribute:
<img src="team-photo.jpg" loading="lazy" alt="Our team at work">That’s it. No JavaScript library needed. The browser handles the rest. It starts loading the image when it’s within a certain distance of the viewport (the visible area of the screen).
When to Use Lazy Loading
- Below-the-fold images. Any image that isn’t visible when the page first loads.
- Image galleries and portfolios. Pages with many images benefit enormously.
- Embedded videos and iframes. YouTube embeds, Google Maps, and similar third-party embeds are expensive to load. Lazy loading them saves significant bandwidth.
- Blog posts with multiple images. Readers may never scroll to the bottom, so there’s no reason to load those images upfront.
When NOT to Use Lazy Loading
- Above-the-fold images. Your hero image, logo, and any images visible on initial load should load immediately. Lazy loading these creates a visible pop-in effect that looks broken.
- LCP (Largest Contentful Paint) element. Whatever your largest visible element is on page load, usually a hero image or heading, should never be lazy loaded. This directly impacts your Core Web Vitals score.
The Intersection Observer API
For more control than native lazy loading provides, the Intersection Observer API lets developers define exactly when an element should be considered “visible” and trigger loading behavior. This is useful for:
- Loading content sections only when they scroll into view
- Triggering animations as elements appear
- Implementing infinite scroll patterns
- Loading heavy components (like interactive maps or data visualizations) on demand
The API watches for elements entering or leaving the viewport and fires a callback when they do. It’s far more performant than the older approach of listening for scroll events, which could fire dozens of times per second and bog down the main thread.

Defer: Load It Later, Run It in Order
The defer attribute is specifically for JavaScript files, and it solves a critical problem: scripts in the <head> of your page that block rendering.
What Defer Does
When you add defer to a script tag, the browser does two things differently:
- Downloads the script in the background while continuing to parse the HTML. The page doesn’t pause to wait for the download.
- Executes the script only after the HTML is fully parsed but before the
DOMContentLoadedevent fires.
<script src="analytics.js" defer></script>The critical detail: deferred scripts execute in order. If you have three deferred scripts, they’ll always run in the order they appear in your HTML. This makes defer safe for scripts that depend on each other.
When to Use Defer
- Analytics scripts. Google Analytics, Facebook Pixel, and similar tracking scripts don’t need to run before the page is visible.
- Third-party widgets. Chat tools, review widgets, social media embeds.
- Your main application JavaScript. In most cases, your site’s JavaScript can be deferred without any impact on functionality.
- Any script that needs the full DOM available. Because defer waits until the HTML is parsed, the entire DOM tree is available when the script runs.
Async: Load It Later, Run It Immediately
The async attribute also downloads scripts in the background, but with one key difference from defer: async scripts execute immediately after they finish downloading, regardless of whether the HTML has finished parsing and regardless of the order they appear in your code.
<script src="third-party-widget.js" async></script>The Difference Between Defer and Async
This is the question we get asked most, so let’s make it crystal clear:
Think of it this way: defer is polite. It waits its turn. Async is impatient: it runs the moment it’s ready, even if it interrupts something else.
When to Use Async
- Truly independent scripts. Scripts that don’t depend on other scripts and don’t need the DOM to be fully loaded.
- Third-party scripts you can’t control. Ad scripts, A/B testing tools, and similar services that manage their own initialization.
- Scripts where execution order doesn’t matter. If loading order is irrelevant, async gives the fastest possible execution.
When to Avoid Async
- Scripts that depend on other scripts. If Script B needs a function defined in Script A, async might load B before A. Use defer instead.
- Scripts that manipulate the DOM. If the script tries to find and modify HTML elements that haven’t been parsed yet, it’ll fail silently or throw errors.
- jQuery and jQuery-dependent scripts. A classic mistake: loading jQuery with async, then loading a plugin that depends on jQuery. The plugin loads first, can’t find jQuery, and breaks.
Common Implementation Mistakes
We’ve audited hundreds of websites, and these are the performance mistakes we see most frequently.
Lazy Loading Everything, Including Above-the-Fold Content
Adding loading="lazy" to every image on the page seems like a quick win, but it actively hurts performance for images that are visible on page load. The browser adds a slight delay before starting the download of lazy-loaded images, which pushes your LCP score higher (worse).
Fix: Audit your above-the-fold content on both desktop and mobile. Any image visible on initial load should use loading="eager" (the default) or have no loading attribute at all.
Using Both Defer and Async
Adding both attributes to a single script tag doesn’t give you “double optimization.” The browser will use async and ignore defer. This usually isn’t what you want.
Fix: Choose one based on whether you need guaranteed execution order (defer) or fastest possible execution (async).
Deferring Critical Scripts
Some scripts need to run before the page is interactive. If your site relies on JavaScript to render navigation, handle form validation, or display critical content, deferring those scripts creates a flash of broken or unstyled content.
Fix: Identify which scripts are genuinely critical for initial render. Keep those loading normally (or inline them). Defer everything else.
Not Specifying Image Dimensions
When you lazy load images without specifying width and height attributes, the browser doesn’t know how much space to reserve for them. As images load while the user scrolls, the page layout shifts: text jumps around, buttons move, and users accidentally click the wrong thing. Google measures this as Cumulative Layout Shift (CLS), and it directly affects your Core Web Vitals score.
Fix: Always include width and height attributes on images, or use CSS aspect-ratio to reserve the correct space.
Forgetting About Mobile
A page that loads fine on your office desktop with a fiber internet connection might take 8 seconds on a phone with a spotty cellular signal. Mobile users are the majority of traffic for most local businesses, and they’re the ones most affected by poor loading strategies.
Fix: Test on real devices with throttled connections. Chrome DevTools lets you simulate 3G and 4G speeds. Your optimization strategy should prioritize the mobile experience.
The Impact on Core Web Vitals
Google uses three Core Web Vitals metrics as ranking signals, and every technique in this article directly affects them:
These aren’t abstract technical scores. They directly affect your Google rankings and, more importantly, your visitors’ experience on your site. A site that loads in 2 seconds instead of 5 doesn’t just rank better. It converts better. Visitors stay longer, read more, and are more likely to fill out a contact form or make a purchase.
Putting It All Together
A well-optimized loading strategy typically looks like this:
- Critical CSS is inlined in the
<head>so the page can render immediately - Above-the-fold images load normally with explicit dimensions
- Below-the-fold images use
loading="lazy"with width and height attributes - Essential JavaScript loads with
deferto maintain execution order without blocking rendering - Independent third-party scripts load with
async - Heavy embeds (maps, videos, chat widgets) are lazy loaded with Intersection Observer
- Non-critical CSS (for below-the-fold components) is loaded asynchronously
The result is a page that feels instant to the user: the content they came for appears quickly, and everything else loads quietly in the background.
Keep Reading
- Local SEO for Service-Area Businesses: Ranking Without a Storefront
- API Integrations 101: Connecting Your Website to the Tools You Already Use
- Font Loading Strategies That Won’t Tank Your Page Speed
These loading patterns are standard in our builds. Our Hot Dog Business Growth case study shows the results on a real consulting site.