search

How to Defer JavaScript Execution and Improve Loading Times?

I often get asked how to defer JavaScript loading on WordPress sites. The main reason you’d want to do this is to improve page speed and make sure content is visible before scripts run.

Since WordPress 6.3, you can set the loading strategy (defer or async) directly when enqueuing your scripts. That’s the recommended approach for new projects. The manual methods below are still useful for legacy code or when you need finer control.

If you search Google, you’ll find plenty of solutions – some work, some don’t. This is one of those gray areas in WordPress performance optimization.

In this post, I’ll cover two proven ways to defer JavaScript on your WordPress site. Caching plugins like WP Rocket handle this automatically, so if you’re already using one, you may not need manual code.

Why Defer JavaScript Loading?

JavaScript files load on nearly every page of your site. Each time the browser encounters a <script> tag, it pauses HTML parsing to fetch and execute that script – blocking the content your visitors actually came for.

Your job as a developer is to make sure the critical content loads first. Share buttons, widgets, analytics, and comment scripts can wait.

The goal: show visitors the text (usually what they care about) and defer script execution until after the content is rendered. Tools like Largest Contentful Paint (LCP) measurements in Google PageSpeed Insights will flag render-blocking scripts as a problem.

If you see warnings like “Defer loading of JavaScript” or “Remove Render-Blocking JavaScript,” this guide will help you fix them.

Before we start, there are three ways a browser can load scripts. The right choice depends on the script’s role and dependencies. The diagrams below make the difference clear:

defer-vs-async-map

Default <script>

The browser parses the HTML until it hits a <script> tag. Parsing stops, the script downloads and executes, and only then does parsing resume.

defer-vs-async

<script async>

The browser downloads the script in parallel with HTML parsing. Once downloaded, parsing pauses while the script runs. Execution order is not guaranteed – whichever script finishes downloading first runs first.

defer-vs-async

<script defer>

The browser downloads the script in parallel with parsing and executes it only after the HTML is fully parsed. Unlike async, defer preserves execution order – scripts run in the sequence they appear in the document.

script-defer

How to Defer JavaScript Loading?

As always, test every change. If a specific script breaks after adding defer, exclude it. There are two main ways to defer JavaScript on a WordPress site:

1. The Modern Way: strategy Parameter (WordPress 6.3+)

If you’re running WordPress 6.3 or later, this is the approach you should use. Pass a strategy key in the $args array when enqueuing:

wp_enqueue_script(
    'my-script',
    get_stylesheet_directory_uri() . '/js/my-script.js',
    array(),
    '1.0.0',
    array(
        'in_footer' => true,
        'strategy'  => 'defer',
    )
);

WordPress handles the dependency tree for you – if script A depends on script B, both get the correct loading attribute automatically. No hacks needed.

To use async instead, change 'strategy' => 'defer' to 'strategy' => 'async'.

Starting with WordPress 6.4, core and bundled theme scripts use the defer strategy and load in the <head> for earlier discovery. The comment-reply script uses async since comments are low priority.

2. Defer All Scripts Except jQuery (Legacy Method)

This method uses the clean_url filter to inject the defer attribute. It works, but it’s a hack – it manipulates URL output rather than the script tag itself. I used it for years before WordPress 6.3, and you’ll still find it on many sites.

Add this to your functions.php:

/* defer all js except jquery.min.js */
if ( !is_admin() ) {
    function defer_parsing_of_js ( $url ) {
        if ( FALSE === strpos( $url, '.js' ) ) return $url;
        if ( strpos( $url, 'jquery.min.js' ) ) return $url;

        // return "$url' defer ";
        return "$url' defer onload='";
    }
}
if ( !is_admin() ) {
    add_filter( 'clean_url', 'defer_parsing_of_js', 11, 1 );
}

Check the jQuery filename your theme loads. Sometimes it’s just jquery.js – adjust the strpos check accordingly.

This adds defer to every script tag except jQuery:

Defer-JavaScript-Loading-in-WordPress-Speed
For async loading, swap “defer” for “async” on line 10.

3. Defer Loading of Scripts by Handle

WordPress 4.1 introduced the script_loader_tag filter, which gives you cleaner control per script handle. If you can’t use the strategy parameter (pre-6.3 sites), this is the next best option:

function add_defer_attribute($tag, $handle) {
    if ( 'my-js-handle' !== $handle )
        return $tag;
    return str_replace( ' src', ' defer src', $tag );
}
add_filter('script_loader_tag', 'add_defer_attribute', 10, 2);

Change the handle in line 2 to match the one used when you enqueue the script. For example:

wp_register_script('my-js-handle', $src, $deps, $ver, $in_footer);

For async loading, change “defer” to “async” in line 4.

What if You Want to Defer More than One Script?

To defer multiple scripts with script_loader_tag, build an array of handles and loop through it. Add this to your functions.php with your handles in line 2:

function add_defer_attribute($tag, $handle) {
   $scripts_to_defer = array('my-js-handle', 'another-handle');
   foreach($scripts_to_defer as $defer_script) {
      if ($defer_script !== $handle) return $tag;
      return str_replace(' src', ' defer src', $tag);
   }
   return $tag;
}
add_filter('script_loader_tag', 'add_defer_attribute', 10, 2);

Defer vs. Delay – What’s the Difference?

You’ll see some performance plugins offer a “Delay JavaScript” option alongside defer. These are different things:

Defer downloads the script in parallel and executes it after the HTML is parsed. The script still runs on every page load – just later.

Delay (sometimes called “lazy load JS”) doesn’t load the script at all until a user interaction occurs – a scroll, click, or keypress. This removes the script entirely from the initial load path, which can dramatically improve Largest Contentful Paint and Interaction to Next Paint (INP) scores.

The trade-off: delayed scripts may cause a brief flash of unstyled content or missing functionality until the user interacts. Defer is the safer default for application code. Delay works best for non-essential third-party scripts like analytics, chat widgets, and social embeds.

If you’re using YouTube embeds, check out the guide on deferring JavaScript for YouTube videos.

Which Method Should You Use?

Here’s a quick decision guide:

WordPress 6.3+: Use the strategy parameter when enqueuing. It’s the cleanest, most reliable approach.

Pre-6.3 sites: Use the script_loader_tag filter for per-handle control.

Quick blanket fix: The clean_url method still works if you need to defer everything at once, but migrate to the strategy parameter when possible.

Non-essential scripts: Consider lazy loading JavaScript or delaying execution until user interaction for things like analytics, chat, and share buttons.

You can also preload critical assets to further optimize the loading waterfall.

FAQs

Common questions about deferring JavaScript in WordPress:

Should I use defer or async for WordPress scripts?
Use defer for most WordPress scripts. It preserves execution order and waits until the DOM is ready, which prevents dependency issues. Use async only for independent scripts that don't rely on other code, like analytics or tracking pixels.
Can I defer jQuery in WordPress?
Generally, no. Many WordPress plugins and themes depend on jQuery being available immediately. Deferring it can break inline scripts that call jQuery() or $() before the deferred file loads. The code examples in this guide intentionally exclude jquery.min.js from deferral.
Do I need to defer JavaScript if I use a caching plugin?
Most caching plugins like WP Rocket, LiteSpeed Cache, and W3 Total Cache have built-in options to defer or delay JavaScript. If you've enabled those settings, adding manual defer code is redundant and can cause conflicts. Check your plugin's JS optimization settings first.
What is the strategy parameter in WordPress 6.3?
WordPress 6.3 added a strategy key to the $args array in wp_enqueue_script() and wp_register_script(). Set it to 'defer' or 'async' to control script loading natively. WordPress handles the dependency chain automatically, making script_loader_tag filters unnecessary for most cases.
Is the clean_url filter method safe to use?
It works, but it's a hack. The clean_url filter was designed for URL sanitization, not for injecting HTML attributes. It can conflict with other plugins that also filter URLs. For new projects, use the strategy parameter (WordPress 6.3+) or the script_loader_tag filter instead.
How do I check if deferring JavaScript actually improved performance?
Run your site through Google PageSpeed Insights before and after the change. Look for improvements in LCP (Largest Contentful Paint) and the "Remove render-blocking resources" audit. You can also check the Network tab in Chrome DevTools to confirm scripts have the defer attribute.

In Summary

We covered three ways to defer JavaScript in WordPress – the modern strategy parameter, the legacy clean_url filter, and the script_loader_tag approach. If you’re on WordPress 6.3+, use the built-in strategy parameter and skip the filter hacks entirely.

Deferring scripts is one of the simplest performance wins you can get. Combined with other WordPress speed optimizations, it can noticeably improve your Core Web Vitals scores.

If you have questions or a different approach that works for you, drop a comment below.

Join the Discussion
0 Comments  ]

Leave a Comment

To add code, use the buttons below. For instance, click the PHP button to insert PHP code within the shortcode. If you notice any typos, please let us know!

Savvy WordPress Development official logo