HomeMethod

Knowledge

Web VitalsTechniquesSnippetsAnswersImpactsBottlenecks

Defer JavaScript

Prevent render-blocking scripts from delaying page rendering.

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

  1. 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.

  2. 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.

  3. 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.

  4. 04

    DOMContentLoaded fires after execution

    Unlike async, deferred scripts run before the DOMContentLoaded event. Code that listens to DOMContentLoaded is guaranteed to run after deferred scripts complete.

Code Example

Before
<script src="/wp-content/themes/my-theme/js/app.js"></script>
After
<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 a src). 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.

Test this techniqueScripts DiagnosticsRun the tool

Targeted fixes, not guesswork.

Targeted fixes, not guesswork. We identify which techniques move the needle for your specific WordPress stack.

Start an Engagement
Support our family now