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.

lazy vs async vs defer
lazy
  • Delays off-screen media
  • Used on img and iframe
  • Improves initial load
async and defer
  • 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:

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:

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:

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.

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.

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.

async vs defer
async
  • Loads in parallel
  • Runs when ready
  • Order not guaranteed
defer
  • Loads in parallel
  • Runs after parsing
  • Order preserved

Both avoid parser blocking during download, but execution timing is different.

Featureasyncdefer
Download behaviorDownloads in parallel with HTML parsingDownloads in parallel with HTML parsing
Execution timeRuns as soon as readyRuns after HTML parsing finishes
Execution orderNot guaranteed between multiple async scriptsPreserved in document order
Best use caseIndependent scriptsMain site scripts and ordered dependencies
DOM safetyCan run too earlySafer 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

12. Practice Exercise

Try updating a simple page so that each resource uses the correct loading behavior.

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.