Key takeaways
- A plain
<script src>in the head blocks HTML parsing until it downloads and runs. deferdownloads in parallel and runs after parsing, in order — the best default for your own scripts.asyncdownloads in parallel and runs as soon as it’s ready, in any order — good for independent scripts like analytics.
“Eliminate render-blocking resources” is one of the most common PageSpeed Insights warnings on WordPress sites. Often the fix is a single attribute on a script tag. Understanding the difference between defer and async lets you apply it safely.
What happens with a normal script#
<head>
<script src="/slider.js"></script>
</head>
The browser stops parsing HTML, downloads slider.js, executes it, and only then continues. On a slow mobile connection, that can delay everything visible by a second or more.
The three loading modes#
| Normal | async |
defer |
|
|---|---|---|---|
| Blocks HTML parsing while downloading? | Yes | No | No |
| When does it run? | Immediately | As soon as downloaded (may interrupt parsing) | After HTML is fully parsed, before DOMContentLoaded |
| Execution order preserved? | Yes | No | Yes |
| Can access full DOM? | Only elements above it | Not guaranteed | Yes |
| Best for | Rare: tiny inline scripts that must run first (e.g. theme flash prevention) | Independent scripts: analytics, ads | Site scripts, libraries with dependencies |
Examples#
<!-- Your site code: depends on the DOM and on each other -->
<script src="/vendor/library.js" defer></script>
<script src="/main.js" defer></script>
<!-- Independent third-party analytics -->
<script src="https://www.googletagmanager.com/gtag/js?id=G-XXXX" async></script>
<!-- ES modules are deferred by default -->
<script type="module" src="/app.js"></script>
Exceptions: when blocking is correct#
A tiny inline script that must run before the page paints — like the one that applies dark mode to avoid a white flash — should stay inline and blocking at the top of the head. Keep such scripts to a few lines.
Need a hand with this?
Get a quick, honest review of your website’s SEO.
Beyond defer: delaying third-party scripts#
Even deferred scripts compete for CPU right after load, affecting INP. For non-essential tags (chat widgets, heatmaps, social embeds), load them after the first user interaction or when the browser is idle:
<script>
function loadChat() {
var s = document.createElement('script');
s.src = 'https://chat.example.com/widget.js';
s.async = true;
document.body.appendChild(s);
}
['scroll', 'pointerdown', 'keydown'].forEach(function (e) {
addEventListener(e, loadChat, { once: true, passive: true });
});
</script>
See third-party scripts for a full strategy.
In WordPress#
Since WordPress 6.3, themes and plugins can register scripts with a loading strategy:
wp_enqueue_script( 'my-main', get_template_directory_uri() . '/main.js', array(), '1.0', array( 'strategy' => 'defer', 'in_footer' => true ) );
This theme loads all of its scripts with defer. For plugins that don’t, performance plugins (WP Rocket, LiteSpeed Cache, Perfmatters, FlyingPress) can add defer or delay scripts — always test menus, forms, sliders and checkout afterwards, and exclude scripts that break.
Common mistakes#
- Adding
asyncto jQuery and plugins that depend on it — they may run before jQuery and fail. - Using
deferon inline scripts (it only works on external scripts withsrc). - Delaying scripts needed for above-the-fold content, causing visible pop-in.
- Moving everything to the footer but still blocking — footer scripts without defer still block the end of parsing.
defer vs async FAQ#
Should I put scripts in the head or before </body>?#
With defer, put them in the head so downloads start early. Without it, placing them before </body> was the old workaround.
Can I use both defer and async?#
If both are present, modern browsers use async. Choose one.
Does defer improve SEO?#
Indirectly — faster rendering improves Core Web Vitals and user experience.