Loading performance in HTML: preload, preconnect, async and defer

Control when the browser fetches and runs resources, so the content people need arrives first.

  • Beginner
  • 7 min read
  • Updated 17 Oct 2026

What you will learn

  • Choose between async and defer
  • Use preload and preconnect sparingly
  • Explain what blocks rendering and parsing

Before you start

The browser discovers resources as it reads your HTML from top to bottom. Some of them stop everything until they arrive. A few attributes let you change that order: run scripts without blocking, and tell the browser about important files before it would find them on its own.

What blocks the page

Two kinds of resources can hold up the first view of a page:

  • Stylesheets in the head are render-blocking. The browser waits for them before painting, so people do not see unstyled content.
  • Classic scripts without async or defer are parser-blocking. When the parser meets one, it stops building the DOM, downloads the script, runs it, and only then continues.
HTML
<head>
  <link rel="stylesheet" href="/css/styles.css">
  <!-- Parsing stops here until app.js downloads and runs -->
  <script src="/js/app.js"></script>
</head>

Browsers soften this with a preload scanner that looks ahead in the HTML for resources to start fetching early. But a blocking script still delays everything after it from appearing.

defer and async

ScriptDownloadsRunsOrder
<script src>Immediately, blocking the parserAs soon as it downloadsIn document order
<script src defer>In parallel with parsingAfter the document is parsed, before DOMContentLoadedIn document order
<script src async>In parallel with parsingAs soon as it downloads, pausing the parser brieflyWhichever finishes first
<script type="module" src>In parallel with parsingLike defer, after parsingIn document order

defer: the default choice

HTML
<script src="/js/menu.js" defer></script>
<script src="/js/app.js" defer></script>

Deferred scripts download without blocking, run after the HTML is fully parsed, so every element exists, and run in the order they appear. Use defer for scripts that work with the page: menus, forms, interactive components. Place them in the head so they start downloading early.

async: independent scripts

HTML
<script src="https://analytics.example.com/tag.js" async></script>

Async scripts run the moment they arrive, in any order. Use async only for scripts that do not depend on other scripts or on the DOM being complete, such as analytics or some third-party widgets.

Modules

HTML
<script type="module" src="/js/main.js"></script>

Module scripts are deferred automatically, so defer has no effect on them. You can add async to a module to run it as soon as it and its imports are ready.

defer and async only work on scripts with a src. On an inline script without type="module", they are ignored and the script runs immediately.

Resource hints

Resource hints are link elements that tell the browser about work it will need to do soon.

preconnect

Before downloading anything from a new origin, the browser must look up the domain (DNS), open a connection and negotiate encryption (TLS). That can take noticeable time on slow networks. preconnect starts it early:

HTML
<link rel="preconnect" href="https://cdn.example.com">

Use it for a small number of other origins that serve critical resources early in loading, such as a font or image CDN. Each preconnect uses resources, and unused connections are closed after a few seconds, so preconnecting to many origins wastes effort.

Font files are always fetched in CORS mode, so when preconnecting to a font origin, add crossorigin:

HTML
<link rel="preconnect" href="https://fonts.example.com" crossorigin>

dns-prefetch

dns-prefetch does only the DNS lookup. It is cheaper than preconnect, so it suits origins that are useful but less critical:

HTML
<link rel="dns-prefetch" href="https://widgets.example.com">

preload

preload fetches a specific file early, at high priority, for use on this page. It is for critical resources the browser would otherwise discover late, such as a font referenced from inside a CSS file, or a hero image set as a CSS background:

HTML
<link rel="preload" href="/fonts/brand.woff2" as="font" type="font/woff2" crossorigin>
<link rel="preload" href="/images/hero.avif" as="image" type="image/avif">
  • as is required. It tells the browser what the resource is, so it applies the right priority, headers and caching. A wrong or missing as can cause the file to download twice.
  • Fonts need crossorigin, even from your own origin, because fonts are fetched in CORS mode.
  • If a preloaded file is not used within a few seconds of the page loading, browsers log a warning in the Console.

For an img in the HTML, fetchpriority="high" on the image itself is usually simpler than a preload, because the preload scanner already finds it. Lesson 15 covers that.

Preload is not free. Everything you preload competes with everything else for bandwidth. Preload one or two genuinely critical, late-discovered files, then measure in the DevTools Network and Performance panels. If nothing improves, remove it.

modulepreload

For JavaScript modules, modulepreload fetches and prepares a module, and can be used for imports that the browser would otherwise find only after loading the main module:

HTML
<link rel="modulepreload" href="/js/components/cart.js">

prefetch

prefetch downloads a resource at low priority for a future navigation, such as the next page in a checkout:

HTML
<link rel="prefetch" href="/checkout/step-2/">

For whole-page prefetching and prerendering, Chromium-based browsers also support the newer Speculation Rules API. Support varies between browsers, so check Baseline and treat it as an enhancement.

A well-ordered head

HTML
<!DOCTYPE html>
<html lang="en-GB">
<head>
  <meta charset="utf-8">
  <meta name="viewport" content="width=device-width, initial-scale=1">
  <title>Riverside Bakery</title>

  <link rel="preconnect" href="https://images.example-cdn.com">
  <link rel="preload" href="/fonts/brand.woff2" as="font" type="font/woff2" crossorigin>

  <link rel="stylesheet" href="/css/styles.css">

  <script src="/js/app.js" defer></script>
  <script src="https://analytics.example.com/tag.js" async></script>

  <meta name="description" content="Fresh bread and baking classes in Bristol.">
  <link rel="icon" href="/icon.svg" type="image/svg+xml">
</head>
<body>
  <h1>Riverside Bakery</h1>
  <img src="https://images.example-cdn.com/hero.avif" alt="Fresh loaves on the bakery counter" width="1600" height="900" fetchpriority="high">
</body>
</html>

Common mistakes

MistakeFix
Blocking scripts at the top of the headAdd defer
async on scripts that depend on each otherUse defer to keep the order
Preloading many files "just in case"Preload only critical, late-discovered files
Preload without as, or a font preload without crossoriginAdd both; otherwise the file may be fetched twice
Preconnect to origins used late or not at allRemove it, or use dns-prefetch

Summary

  • Stylesheets block rendering; classic scripts without async or defer block parsing.
  • Use defer for most scripts, async for independent ones, and remember modules are deferred by default.
  • preconnect warms up connections to a few critical origins; dns-prefetch is a cheaper alternative.
  • preload fetches critical, late-discovered files for this page; always set as, and crossorigin for fonts.
  • Measure in DevTools after adding any hint, and remove hints that do not help.

You finished the course

You can now build complete, semantic HTML pages: well-structured documents, text, links, images, media, tables and forms, plus native interactive elements, safe embeds, sharing metadata and loading hints. Take the final quiz to check your understanding.

Then continue with the CSS Complete Course to style and lay out the pages you can now build, and Web Accessibility Essentials to go deeper into the accessibility foundations this course introduced.