How Browsers Handle Script Loading
When a browser encounters a <script> tag in your HTML, its default behavior is to stop everything — pause HTML parsing, download the script, execute it, and only then continue building the page. This is called render-blocking behavior, and it is one of the most common causes of slow Largest Contentful Paint (LCP) on WordPress sites.
The defer and async attributes on <script> tags change this default behavior, allowing the browser to continue parsing HTML while scripts download in the background. Understanding the difference between them is key to choosing the right loading strategy for each script on your site.
The Default: Parser-Blocking Scripts
A plain <script src="file.js"> tag without any attributes forces the browser to:
- Pause HTML parsing
- Download the JavaScript file
- Execute the JavaScript file
- Resume HTML parsing
Every millisecond spent in steps 1 through 3 is time your visitor spends staring at a blank or incomplete page. If a page loads five parser-blocking scripts, each one creates a sequential bottleneck.
Async: Download in Parallel, Execute Immediately
Adding async tells the browser to download the script in the background while continuing to parse HTML. However, as soon as the download finishes, the browser pauses parsing to execute the script immediately. This means:
- Download does not block parsing (good)
- Execution still blocks parsing (less good)
- Scripts execute in whatever order they finish downloading — not the order they appear in HTML
Async is best suited for independent scripts that do not depend on other scripts or the DOM being fully built. Analytics snippets and tracking pixels are common candidates.
Defer: Download in Parallel, Execute After Parsing
The defer attribute also downloads scripts in the background, but delays execution until the HTML is fully parsed. Deferred scripts:
- Download in parallel with HTML parsing
- Execute after the DOM is complete, but before the
DOMContentLoadedevent - Execute in the order they appear in the HTML
Defer is the safer default for most WordPress scripts. It preserves execution order (important when scripts depend on each other, like jQuery and its plugins) while removing the parsing delay.
WordPress and Script Loading
Since WordPress 6.3, core adds the defer attribute to scripts registered through the standard wp_enqueue_script() system using a strategy parameter. However, many plugins and themes still enqueue scripts without specifying a loading strategy, which means those scripts load in the default parser-blocking way.
Some plugins also inject inline scripts or use wp_head hooks that bypass the enqueue system entirely. These scripts cannot benefit from defer/async unless the plugin developer updates their approach.
When Defer Can Cause Problems
Deferring a script that expects to run before the page renders — for example, scripts that modify the DOM during load to prevent a flash of unstyled content — can cause visual glitches. Some jQuery-dependent code also assumes jQuery is available immediately in the <head>, and deferring jQuery breaks that assumption. Testing after changing script loading behavior is important.
Tools That Can Help
Autoptimize can add defer to all aggregated JavaScript automatically. It provides a simple toggle but does not give per-script control, which can cause issues with scripts that need to execute early.
Perfmatters and Asset CleanUp both provide per-script control over defer and async attributes, along with the ability to disable scripts on specific pages. This granularity is more work to configure but less likely to break things.
Further Reading
- Efficiently Load Third-Party JavaScript (web.dev) — Covers async, defer, and other strategies for loading external scripts without hurting performance.
- The Script Element: defer (MDN) — Technical reference for the defer attribute and how browsers implement it.
