Defer JavaScript
Prevent render-blocking scripts from delaying page rendering.
Metrics
This technique can move up to 33% of LCP's share of the Lighthouse score.
This technique can move up to 100% of TBT's share of the Lighthouse score.
This technique does not move CLS.
This technique can move up to 67% of FCP's share of the Lighthouse score.
This technique can move up to 33% of SI's share of the Lighthouse score.
This technique does not move TTFB.
Overview
The defer attribute on a <script> tag tells the browser to download the script in the background while HTML parsing continues, then execute it only after the document has been fully parsed. It costs a single HTML attribute — and it is one of the highest-impact, lowest-risk optimisations available for script-heavy WordPress pages.
Impact grade, A+ to D: how much of the Lighthouse score this technique can move. Unaffected: CLS, TTFB.
The Problem
By default, when a browser encounters a <script> tag it stops parsing HTML, downloads the script, executes it, then resumes. This blocking behaviour directly delays First Contentful Paint and inflates Total Blocking Time. On a typical WordPress page with 10–20 third-party scripts, this stall can add hundreds of milliseconds of invisible dead time before the user sees anything.
Performance Impact
LCP
100–400ms
indirect — parser unblocked earlier frees network bandwidth for LCP resource; can regress if LCP element is JS-rendered
TBT
40–80%
typical reduction for script-heavy pages
FCP
200–600ms
depends on script weight and server RTT; ~295–340ms avg observed in real-world audits
SI
100–300ms
visual population starts earlier once render-blocking is eliminated
INP
varies
less early main-thread saturation leaves more budget for interaction callbacks; async execution of deferred scripts can still spike INP post-load
Risk: Low
safe for most pages; can delay LCP if JS renders the LCP element; breaks execution order when inline or async scripts depend on deferred files
How it works
01
Browser encounters the script tag
When the HTML parser reaches a
<script defer src="…">tag, it registers the request and immediately continues parsing the rest of the document — no pause.02
Script downloads in parallel
The browser fetches the script file over the network while simultaneously building the DOM from the remaining HTML. Bandwidth is shared, but the main thread keeps working.
03
Execution waits for DOM ready
Once HTML parsing completes and the DOM is fully built, deferred scripts execute in the order they appeared in the document — preserving dependency order without the blocking penalty.
04
DOMContentLoaded fires after execution
Unlike
async, deferred scripts run before theDOMContentLoadedevent. Code that listens toDOMContentLoadedis guaranteed to run after deferred scripts complete.
Code Example
<script src="/wp-content/themes/my-theme/js/app.js"></script><script src="/wp-content/themes/my-theme/js/app.js" defer></script>In WordPress, add defer in wp_enqueue_script() using the $args array (WP 6.3+) or via the script_loader_tag filter for older versions.
Limitations
- Has no effect on inline scripts (
<script>…</script>without asrc). Those must be moved to external files first. - Scripts that manipulate the DOM during initial load (e.g.,
document.write()) will break with defer — audit before applying blindly. - Does not apply to module scripts (
type="module"), which are deferred by default. - Browser support is universal, but the exact execution timing can vary slightly between engines — always test after applying.
Common Mistakes
Deferring scripts that depend on synchronous inline scripts
If a deferred script relies on a variable set by an inline script that runs later, you will get race conditions. Audit dependencies or inline the critical variable in a deferred wrapper.
Using defer AND async on the same script
When both are present, async takes precedence in modern browsers and defer is ignored. Pick one: defer preserves order, async does not.
Deferring scripts in the <body> already loaded last
Scripts at the very end of <body> are effectively already deferred (DOM is mostly ready). Applying defer there adds no measurable benefit — focus on scripts in <head>.
Assuming defer fixes all TBT issues
Defer eliminates download blocking, but long-running JavaScript still occupies the main thread post-parse. Defer plus code-splitting and removing unused scripts is the full answer.
Targeted fixes, not guesswork.
Targeted fixes, not guesswork. We identify which techniques move the needle for your specific WordPress stack.
Start an Engagement