HTML Loading Attributes: lazy, async, and defer Explained
HTML loading attributes help the browser decide when to fetch and execute certain resources. In practice, that means you can make pages feel faster, reduce unnecessary network work, and avoid blocking the user interface while scripts or media load. This article explains what loading="lazy", async, and defer do, how they differ, and how to use them correctly in real HTML documents.
Quick answer: Use loading="lazy" for off-screen images and iframes so they load when needed, not immediately. Use defer for most external scripts that should wait until HTML parsing finishes, and use async for independent scripts that can load and run whenever they are ready.
Difficulty: Beginner
Helpful to know first: basic HTML document structure, how elements like img and script work, and the idea that browsers download page resources as they parse HTML.
1. What Is HTML Loading Attributes?
HTML loading attributes are built-in ways to influence resource loading behavior directly in markup. They are especially important for performance because some resources can be delayed safely, while others must be available immediately.
- Delays off-screen media
- Used on img and iframe
- Improves initial load
- Used on external scripts
- Reduce parser blocking
- Different execution timing
These attributes solve related performance problems, but they apply to different HTML elements.
The three names in this topic are related, but they are not interchangeable:
- loading="lazy" is used on elements such as img and iframe to delay loading until the resource is closer to being needed.
- async is used on external script elements so the script downloads in parallel and executes as soon as it finishes downloading.
- defer is also used on external script elements, but the script waits to execute until after the HTML document has been parsed.
- async and defer affect script timing. loading="lazy" affects when images or iframes are fetched.
- These attributes improve performance only when they match the actual importance of the resource.
Beginners often group these together because they all seem to mean “load later.” That is only partly true. The real difference is what resource is being loaded and when the browser is allowed to fetch or execute it.
2. Why HTML Loading Attributes Matters
Without careful loading behavior, a page can spend time downloading and processing resources that the user does not need yet. That hurts metrics such as page speed, responsiveness, and perceived performance.
These attributes matter because they help solve common performance problems:
- Large below-the-fold images can slow the initial page load even though the user cannot see them yet.
- Embedded iframes, such as videos or maps, can be expensive to load early.
- Traditional scripts without async or defer can block HTML parsing and delay page rendering.
- Third-party scripts such as analytics or ads often do not need to pause document parsing.
- Dependent scripts can break if they execute in the wrong order, so choosing the right attribute affects both speed and correctness.
In real projects, these small decisions add up. A page with many images, embeds, and scripts can feel dramatically faster when the browser is allowed to prioritize the most important content first.
Loading attributes are performance tools, not magic fixes. If a resource is critical for the first screen of the page, delaying it can make the experience worse instead of better.
3. Basic Syntax or Core Idea
This section shows the minimal syntax for each attribute and explains what the browser does with it.
Using loading="lazy" on images
Add the loading attribute to an image when the image is not immediately needed on first render.
<img src="gallery-photo.jpg" alt="Mountain trail at sunrise" width="800" height="600" loading="lazy">Here, the browser may delay downloading the image until it is near the viewport. The alt text remains important for accessibility, and the explicit width and height help prevent layout shifts while the image loads.
Using loading="lazy" on iframes
This attribute can also be useful for embedded content that is heavy or not needed immediately.
<iframe src="/example/map.html" title="Store location map" width="600" height="400" loading="lazy"></iframe>This allows the browser to postpone loading the embedded page until it is more likely to be viewed.
Using async on scripts
Use async only on external scripts loaded with src. The browser downloads the script while parsing the HTML, and executes it as soon as the download finishes.
<script src="analytics.js" async></script>This is good for scripts that do not depend on the order of other scripts and do not need the whole document to finish parsing first.
Using defer on scripts
Like async, defer downloads the script without blocking HTML parsing. The difference is execution timing: deferred scripts wait until parsing is complete.
<script src="app.js" defer></script>This is usually the safer choice for page behavior scripts because the document structure is ready by the time the script runs.
Why async and defer are often confused
Both attributes prevent a classic parser-blocking script download, but their execution rules are different:
- async: download in parallel, execute as soon as ready, order is not guaranteed.
- defer: download in parallel, execute after parsing, order is preserved among deferred scripts.
That difference is the main reason developers choose one over the other.
4. Step-by-Step Examples
The examples below show how these attributes work in realistic page situations.
Example 1: Lazy loading images in a long article
If an article has many images, only the first visible ones should usually load immediately. The rest can wait.
<article>
<h2>Travel Guide</h2>
<p>Introduction text for the guide.</p>
<img src="cover.jpg" alt="City skyline at sunset" width="1200" height="700">
<img src="street-market.jpg" alt="Outdoor street market" width="800" height="600" loading="lazy">
<img src="museum.jpg" alt="Museum entrance" width="800" height="600" loading="lazy">
</article>The first image is likely part of the initial view, so it loads normally. The later images use lazy loading so the browser can prioritize the visible content first.
Example 2: Lazy loading an embedded video section
Embedded content can be much heavier than a normal image. Delaying it often improves first load performance.
<section>
<h2>Watch the product demo</h2>
<p>Scroll down to watch the full demonstration video.</p>
<iframe
src="/example/video-player.html"
title="Product demonstration video"
width="800"
height="450"
loading="lazy">
</iframe>
</section>This keeps the iframe from competing with more important content at the top of the page. The title attribute also makes the iframe more accessible to assistive technologies.
Example 3: Using async for an independent analytics script
Some scripts do not need to touch the page structure immediately and do not depend on other scripts. These are often good candidates for async.
<head>
<title>Storefront</title>
<script src="analytics.js" async></script>
</head>The browser can download the analytics file while continuing to parse the page. If the file finishes early, it may execute before parsing is complete, which is acceptable only if the script does not rely on document order or specific elements being ready.
Example 4: Using defer for page behavior scripts
Most site scripts that work with navigation, forms, or page content are safer with defer.
<head>
<title>Documentation Page</title>
<script src="vendor.js" defer></script>
<script src="app.js" defer></script>
</head>Both scripts download in parallel, but they execute after parsing finishes. Their order is preserved, so vendor.js runs before app.js. That makes defer a strong default for scripts with dependencies.
5. Practical Use Cases
Here are specific situations where these loading attributes are useful.
- Use loading="lazy" for article images that appear well below the first screen of content.
- Use loading="lazy" for map, video, or social embed iframes placed farther down the page.
- Use defer for your main site scripts that need the DOM structure to exist before they run.
- Use defer for multiple related external scripts where execution order must stay predictable.
- Use async for independent third-party scripts such as analytics when script order does not matter.
- Avoid lazy loading above-the-fold hero images, logos critical to branding, or media the user should see immediately.
- Avoid async for scripts that depend on each other or on HTML elements that may not exist yet when the script runs.
In most everyday pages, a simple rule works well: lazy-load noncritical media, defer most external scripts, and reserve async for scripts that are truly independent.
6. Common Mistakes
Loading attributes are simple to write, but beginners often apply them in the wrong place or expect them to solve problems they were not designed to solve.
Mistake 1: Lazy-loading the most important image
A hero image, product image at the top of the page, or logo that appears immediately should usually load as soon as possible.
Problem: This code delays an above-the-fold image that the user is supposed to see right away, which can make the page feel slower instead of faster.
<img src="/images/hero.jpg" alt="Main product photo" loading="lazy">Fix: Leave the loading attribute off for critical images, or use the browser default eager behavior.
<img src="/images/hero.jpg" alt="Main product photo">The corrected version works because the browser treats this image as important content that should load without lazy delay.
Mistake 2: Using async for scripts that depend on order
The async attribute does not preserve script order. A later file can finish before an earlier one.
Problem: This setup may fail because app.js could run before helpers.js has loaded and executed.
<script src="/js/helpers.js" async></script>
<script src="/js/app.js" async></script>Fix: Use defer when scripts are related and need predictable execution after HTML parsing.
<script src="/js/helpers.js" defer></script>
<script src="/js/app.js" defer></script>The corrected version works because deferred scripts keep document order and run after parsing is complete.
Mistake 3: Expecting defer to work on inline scripts
The defer attribute is meant for external scripts with a src attribute. It does not make an inline script delay in the same way.
Problem: This code looks like it should be deferred, but the browser does not treat an inline script with defer as a deferred external file.
<script defer>
document.querySelector("h1").
textContent = "Updated title"
</script>Fix: Either move the code into an external script file with defer, or place the inline script after the HTML it depends on.
<h1>Original title</h1>
<script>
document.querySelector("h1").
textContent = "Updated title"
</script>The corrected version works because the heading already exists in the document by the time the script runs.
Mistake 4: Assuming lazy loading works for every situation
Native lazy loading is widely supported in modern browsers, but behavior can vary depending on browser support, layout, and how the element enters the viewport.
Problem: This code may not behave as expected if you rely on lazy loading as your only performance strategy or test in an environment with limited support.
<img src="/images/gallery-1.jpg" alt="Gallery image" loading="lazy">Fix: Treat loading="lazy" as a helpful browser hint, and still optimize image size, dimensions, and formats.
<img src="/images/gallery-1.webp" alt="Gallery image" width="800" height="600" loading="lazy">The corrected version works better because lazy loading is combined with good media optimization instead of being asked to do everything alone.
7. Best Practices
Good performance comes from using these attributes intentionally, not adding them everywhere by default.
Use defer as the default choice for most external site scripts
If your script needs the DOM and does not need to block page parsing, defer is usually the safest first choice.
<script src="/js/site.js" defer></script>This is a strong default because the browser can keep parsing HTML while still guaranteeing script execution after parsing.
Reserve async for independent scripts
Use async only when the script does not depend on other scripts and the rest of the page does not depend on it finishing in a particular order.
<script src="https://analytics.example.com/track.js" async></script>This keeps the page flexible because the script can load whenever it is ready without blocking other work.
Lazy-load only noncritical media
Lazy loading is best for content the user will probably not see immediately.
<img src="/images/article-photo-3.jpg" alt="Article detail photo" width="1200" height="800" loading="lazy">This improves initial load performance without delaying the most visible content.
Always include image dimensions when possible
Even when lazy loading works, missing dimensions can cause layout shifts as images appear.
<img src="/images/team-photo.jpg" alt="Our team" width="640" height="480" loading="lazy">This helps the browser reserve the right amount of space before the image finishes loading.
8. Limitations and Edge Cases
These attributes are useful, but they do not guarantee identical behavior in every browser or layout situation.
- loading="lazy" is a browser hint, so the browser still decides the exact loading moment.
- Lazy loading should not be used for media that is essential to the first visible screen.
- async can create difficult timing bugs when scripts depend on each other.
- defer applies to external classic scripts and is not a general delay switch for inline code.
- If a script modifies elements that are not in the DOM yet, you may see runtime errors such as Cannot read properties of null because the selector found nothing.
- Third-party scripts may document their own loading requirements, and those instructions should take priority over generic rules.
- Some developers expect lazy loading to reduce file size, but it only changes when loading begins, not how large the resource is.
Tip: If lazy loading seems not to work, check whether the element is already near the viewport, whether the browser supports native lazy loading, and whether the real problem is oversized media rather than loading timing.
9. async vs defer in HTML
This comparison is important because these two attributes are often confused, even though they solve different script-loading problems.
- Loads in parallel
- Runs when ready
- Order not guaranteed
- Loads in parallel
- Runs after parsing
- Order preserved
Both avoid parser blocking during download, but execution timing is different.
| Feature | async | defer |
|---|---|---|
| Download behavior | Downloads in parallel with HTML parsing | Downloads in parallel with HTML parsing |
| Execution time | Runs as soon as ready | Runs after HTML parsing finishes |
| Execution order | Not guaranteed between multiple async scripts | Preserved in document order |
| Best use case | Independent scripts | Main site scripts and ordered dependencies |
| DOM safety | Can run too early | Safer when code needs parsed HTML |
Use async when you want the script to arrive and run as quickly as possible without caring about order. Use defer when you want non-blocking loading but still need predictable execution after the document is parsed.
10. Practical Mini Project
This small page uses all three loading behaviors in sensible places: a normal hero image, deferred site scripts, and lazy-loaded gallery images farther down the document.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Loading Attributes Demo</title>
<script src="/js/navigation.js" defer></script>
<script src="/js/gallery.js" defer></script>
<script src="https://analytics.example.com/track.js" async></script>
</head>
<body>
<header>
<h1>Travel Journal</h1>
<img
src="/images/hero.jpg"
alt="Mountain landscape"
width="1200"
height="700">
</header>
<main>
<p>Introductory content appears here.</p>
<section>
<h2>Photo Gallery</h2>
<img
src="/images/gallery-1.jpg"
alt="Forest trail"
width="800"
height="600"
loading="lazy">
<img
src="/images/gallery-2.jpg"
alt="Lake at sunrise"
width="800"
height="600"
loading="lazy">
</section>
</main>
</body>
</html>This example works well because it does not lazy-load the first important image, it keeps site scripts predictable with defer, and it uses async only for an independent analytics script.
11. Key Points
- loading="lazy" is mainly for noncritical images and embedded content below the initial viewport.
- async is best for independent external scripts that do not rely on order.
- defer is usually the best choice for main site scripts that need the DOM.
- Do not lazy-load above-the-fold images that users should see immediately.
- Do not use multiple async scripts when one depends on another.
- Loading attributes improve timing, but they do not replace file compression, sizing, or general optimization.
12. Practice Exercise
Try updating a simple page so that each resource uses the correct loading behavior.
- Add one top-of-page hero image that should load immediately.
- Add two gallery images lower on the page that should lazy-load.
- Add two site scripts that must run in order after parsing.
- Add one independent analytics script.
Expected output: The hero image loads normally, the lower gallery images use lazy loading, the site scripts use defer, and the analytics script uses async.
Hint: Ask whether each resource is critical, order-dependent, or independent.
<!doctype html>
<html lang="en">
<head>
<meta charset="utf-8">
<title>Practice Solution</title>
<script src="/js/helpers.js" defer></script>
<script src="/js/app.js" defer></script>
<script src="https://analytics.example.com/track.js" async></script>
</head>
<body>
<h1>Photo Blog</h1>
<img
src="/images/hero.jpg"
alt="Hero image"
width="1200"
height="700">
<section>
<h2>Gallery</h2>
<img
src="/images/photo-1.jpg"
alt="Photo 1"
width="800"
height="600"
loading="lazy">
<img
src="/images/photo-2.jpg"
alt="Photo 2"
width="800"
height="600"
loading="lazy">
</section>
</body>
</html>13. Final Summary
HTML loading attributes help you control when the browser fetches and runs important resources. In practice, loading="lazy" is for noncritical media, async is for independent scripts, and defer is the usual choice for site scripts that need reliable order and a fully parsed document.
The biggest mistakes come from using these attributes everywhere without thinking about priority. If an image is central to the first screen, do not lazy-load it. If scripts depend on each other, do not mark them async. If you remember those two rules, you will avoid most real-world problems.
As a next step, pair these attributes with other performance basics such as properly sized images, modern file formats, caching, and reducing unnecessary script weight.