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

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.
<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.
<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.
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 thestrposcheck accordingly.
This adds defer to every script tag except jQuery:
![]()
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:
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.jQuery() or $() before the deferred file loads. The code examples in this guide intentionally exclude jquery.min.js from deferral.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.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.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.

