Tutorial to Add Defer and Async Attributes to Render Blocking Javascript in WordPress

WordPress loads a host of external JavaScript references every time a page renders.

These include the standard scripts added by wordpress and a few that are added by your theme and plugins using the wp_enqueue_scripts function. Depending on the type of script, it may be located within the head, body or footer section of your web pages.

Scripts located within the head and body section of a page can cause page load delays as the browser tries to load and execute these script(s) even before the actual content of the page.

This is why these scripts are referred to as render blocking javascripts.

One way to resolve this issue is to move all your scripts to the footer of the page, but in the event that this is not possible, another option is to add a defer or async attribute to your script tags.

Let's see what these attributes are and how they can help you improve page load times.

What are Async and Defer attributes?

Here's what the async and defer attributes do:

Async Attribute: The async attribute loads the script asynchronously. In other words, ensures that the script loads asynchronously along side the other contents of the page, after which it is executed.

Defer Attribute: The defer attribute defers loading of the script. It ensures that the script is executed only after all contents of the page have finished loading.

Both these attributes are well supported in all modern browsers including Firefox, Chrome and Internet explorer. Internet explorer has added support for these attribute since IE10.

An example of a script tag with the async and defer attributes is as follows:

<script src='http://sitename.com/js/scripts.js' async='async' type='text/javascript'></script>
<script src='http://sitename.com/js/scripts.js' defer='defer' type='text/javascript'></script>

Function to add 'async/defer' attribute to your render blocking scripts

In this article, we are going to look at three different methods to add these attributes to your render blocking javascripts. These methods are as follows:

  • Method 1: Adding defer/async to all scripts without exception.
  • Method 2: Adding defer/async to all scripts with exception of a few.
  • Method 3: Adding defer/async only to selective scripts. (Most flexible method as it allows you to add async to some scripts and defer to others.)

You can use any method that suits your need.

Method 1: Adding Async or Defer to All Scripts

If you would like to add the async or defer attribute to all scripts without exception then you can use the following code.

Open your theme's functions.php page and add this code to the bottom of the page.


/*function to add async to all scripts*/
function js_async_attr($tag){

# Add async to all remaining scripts
return str_replace( ' src', ' async="async" src', $tag );
}
add_filter( 'script_loader_tag', 'js_async_attr', 10 );

Note: The above function will add the async attribute to all scripts. Replace, async="async" with defer="defer" if you would like to use the defer attribute instead.

Method 2: Adding Async or Defer to All Scripts with Exception of a Few

The above method added async or defer attributes to all scripts. If instead you would like to add these attributes to all scripts with the exclusion of a few, you can use this code:


/*function to add async to all scripts*/
function js_async_attr($tag){

# Do not add async to these scripts
$scripts_to_exclude = array('script-name1.js', 'script-name2.js', 'script-name3.js');
 
foreach($scripts_to_exclude as $exclude_script){
	if(true == strpos($tag, $exclude_script ) )
	return $tag;	
}

# Add async to all remaining scripts
return str_replace( ' src', ' async="async" src', $tag );
}
add_filter( 'script_loader_tag', 'js_async_attr', 10 );

Note: Replace, async="async" with defer="defer" if you would like to use defer instead.

Replace script-name1.js, script-name2.js etc. with the names of scripts that you want to exclude. Refer Method 3 below if you don't know how to find the script names.

Method 3: Adding Defer or Async to Specific Scripts

Depending on the script and what it does, you might want to 'defer' them or load them asynchronously.

As mentioned earlier, deferred scripts are executed only after a page has finished loading completely, so if your script needs to be executed during page loads, an async attribute would be more appropriate.

Keeping this in mind, the function below will allow you to add a defer or async attribute to selective scripts.

Let's see how this can be achieved:

Step 1: The first step is to find and make a list of all render blocking scripts that you want to add the defer or async attribute to.

A simple way to do this is to use Google's Page Speed tool or other similar tools like the one offered by GTmetrix.com

Visit any one of these tools and enter the URL to any one of your single post pages and click 'Analyze'. Once the results appear, make a list of scripts that are listed under render blocking javascript.

Another way of-course is to check your webpage's HTML source and then to use 'find' (CTRL+F) to look for all the .js files that appear above the fold.

Step 2: The second step is to find unique script names for all scripts that you want to defer or async.

You can easily do this using Google PageSpeed insights.

Simply check for your scripts under the 'Eliminate render-blocking JavaScript' section. You can use the name of the script as a unique name.

For example: Let's take the following script:

'https://sitename.com/wp-content/plugins/thrive/js/compat.min.js?ver=1.500.18

A unique name to identify the above script would be compat.min.js.

Refer image below for further clarification:

Page speed render blocking js

You can also find the script names by checking your website's HTML source:

To do this, simply open a post page of your blog in your browser and check the HTML source of this page (You can view HTML source of a page by clicking 'CTRL + U' on your keyboard). Once there, use your browser's Find function (CTRL + F) and search for the keyword, script type='text/javascript'. You should now be able to see all your script tags. (refer image below)

Simply copy the name of the script tag and use it as the script name.

As shown in the image below, a unique name for a script is a string that can be used to uniquely identify the script.

View html source to find JS scripts

Step 3: Open you theme's functions.php file and add the following code to the end of the file.

The $scripts_to_defer variable contains an array of scripts that are to be deferred and the $scripts_to_async variable contains an array of scripts to be be loaded asynchronously.

Make sure to edit the code with your script names.

/*function to add async and defer attributes*/
function defer_js_async($tag){

## 1: list of scripts to defer. (Edit with your script names)
$scripts_to_defer = array('script-name1.js', 'script-name2.js', 'script-name3.js');
## 2: list of scripts to async. (Edit with your script names)
$scripts_to_async = array('script-name1.js', 'script-name2.js', 'script-name3.js');
 
#defer scripts
foreach($scripts_to_defer as $defer_script){
	if(true == strpos($tag, $defer_script ) )
	return str_replace( ' src', ' defer="defer" src', $tag );	
}
#async scripts
foreach($scripts_to_async as $async_script){
	if(true == strpos($tag, $async_script ) )
	return str_replace( ' src', ' async="async" src', $tag );	
}
return $tag;
}
add_filter( 'script_loader_tag', 'defer_js_async', 10 );

Code Explanation: This function adds the defer or async attributes to the script tags by adding a filter to the wordpress script_loader_tag.

We start by saving the unique names of scripts that need to use defer and async in an array and then use a foreach loop to run through these arrays. Each time the loop runs, it tries to find the position of the unique filename in the script tags using the strpos (string position) function. If the strpos function returns TRUE (indicating the position of the unique string has been found in the script tag), a defer or async attribute is added using the PHP str_replace (string replace) function. If not, the tag is returned without any modifications.

 

Working Example:

Let's say you want to add a defer attribute to the following scripts:

<script src='http://sitename.com/wp-content/plugins/contact-form-7/includes/js/scripts.js?ver=4.1.2'></script>
<script src='http://sitename.com/wp-content/plugins/powerpress/player.min.js?ver=1429646074'></script>

And add the async attribute to the following scripts:

<script src='http://sitename.com/wp-includes/js/comment-reply.min.js?ver=4.2'></script>
<script src='http://sitename.com/wp-content/themes/twentytwelve/js/navigation.js?ver=20140711'></script>

A unique name to identify the first two scripts would be: contact-form-7 and powerpress/player.min.js. Unique names to identify the last two scripts would be: comment-reply.min.js and twentytwelve/js/navigation.js

Once you get the names, you can add them to the above code as follows:

## 1: list of scripts to defer.
$scripts_to_defer = array('contact-form-7', 'powerpress/player.min.js');
## 2: list of scripts to async.
$scripts_to_async = array('twentytwelve/js/navigation.js', 'comment-reply.min.js');
Note: Make sure to wrap the script names in single quotes and separate them with a comma. You can add as many names as you want using this method.

If you do not have any script to defer then you can leave it as a blank array as follows and vice versa:

## 1: list of scripts to defer.
$scripts_to_defer = array();
## 2: list of scripts to async.
$scripts_to_async = array('twentytwelve/js/navigation.js', 'comment-reply.min.js');

Once done, save the functions.php file and check the source of your page to ensure that the defer and async attributes have been added.

Important Note: If you use the 'WP Super Cache' plugin, then make sure to delete cache after adding the code to your functions.php file. Check back using Google Pagespeed after that. If you don't delete the cache, your changes won't be visible for at-least another 5 to 6 hours.
Important Note 2: Check the HTML source (CTRL + U) of your pages to ensure that they attributes have been added.

Also Read: How to prevent wordpress plugins from loading JS and CSS on all pages.

References:
StackExchange - How to Add Defer Tag for Plugins
Google Pagespeed Insights - Remove Render Blocking javascript

 
 
 
 

Comments

  1. DB says:

    Thank you for this article.

    I tried "Method 2: Adding Async or Defer to All Scripts" and it worked.

    Any thoughts on how to get rid of "Render-blocking CSS" by adding code to "Function.php".

    Regards.

    • Mukesh says:

      Hi DB, it is not possible to rectify the render-blocking CSS issue using a function. The only thing that can be done is use a minification service like cssminifier.com to minify your CSS so it loads faster.

      Another thing that can be done is to use separate CSS stylesheets that is only requested when required. Like a separate css for media queries that only loads when the site is opened on a smaller screen. But in most cases this is an overkill and can make it very difficult when it comes to editing your CSS files. As long as you take care of your JS files, you are good to go.

  2. thu thuat says:

    Best article, very very detail...thanks so much

  3. Bob says:

    Came across this article while looking to defer some JS files on page load to speed site up, and its one of the best articles explaining how the defer and async work, so thanks :)

    I have a question, I have this little script going and its working fine front-side, but the admin area does not load some theme plugins im using, like Yoast or Visual Composer.

    Is there a way to place the is_admin into this code so that the admin area loads as normal

    function js_defer_attr($tag){

    # Do not add async to these scripts

    $scripts_to_exclude = array('jquery.js');

    foreach($scripts_to_exclude as $exclude_script){
    if(true == strpos($tag, $exclude_script ) )
    return $tag;
    }

    # Add async to all remaining scripts
    return str_replace( ' src', ' defer="defer" src', $tag );
    }
    add_filter( 'script_loader_tag', 'js_defer_attr', 10 );

    • Mukesh M says:

      Hi Bob, Just replace the last line in the code with the following:

      if( ! is_admin() ){add_filter( 'script_loader_tag', 'js_defer_attr', 10 );}

  4. Jay says:

    Hey Mukesh,

    I just used this function to add an async tag to one of the JS files loaded in the header. However, now the plugin that calls it does not work correctly. I need to remove the async tag, but I cannot figure out how to do it and I can't find any code in the article to undo the async attribute and return the JS call back to how it was. Can you please help?

    Thank you!

    Jay

    • Mukesh M says:

      Hi Jay, just remove the function you added. Or use Method 3 to adding async only to specific scripts. This way you can avoid adding async to the scripts loaded by that particular plugin. Send me an email if you are not able to resolve this. Will help you out.

  5. Gulshan Kumar says:

    Omg! I messing from myself. Why I didn't find this awesome article before!

    Actually, I was looking for solution to prevent from render-blocking. I just tested method 3, it's worked successfully for me as exactly I wanted.

    One little suggestion: It would great, if could specify a line which browser support async and which one defer. I hope, it will clear what we should prefer. Basically, I'm using 'async defer' both.

    Anyway, Your article made my day successful. Thank you so much @Mukesh
    I really appreciate your efforts that you put to write this awesome content.
    Please keep it up.

  6. Việt Hùng says:

    Thank you very much :)

  7. Rony says:

    hi .. when i try to add the code ( method - 1 ) in function.php it says " parse error syntax error unexpected '*' in functions.php " ... what should i do ? .. i'm not a coder .. plz help .. give me proper instruction where should i put the script

    • M Mukesh says:

      Rony, I rechecked the code and all seems to work fine for me. Make sure that you copied the code properly. Try pasting it to the very end of your functions.php page.

  8. Elliott says:

    I added method 1 to a child theme function.php file and it's done something interesting to the site. 100% load speeds and good page insight scores. Im also getting a CANNOT MODIFY HEADER warning shown as the page preview in the pingdom window instead of my homepage. Is there a way to UNDO method 1. I removed it from the function.php file but im still having issues.

    • M Mukesh says:

      Hi Elliott,

      Removing the code from functions.php is all that is required. If you are still receiving an error, it could be because of some other issue.

  9. Thank you for your post, is there something similar for CSS?

Leave a Reply

Your email address will not be published. Required fields are marked *