Add Defer & Async Attributes to WordPress Scripts

WordPress developers often enqueue scripts incorrectly and sometimes it was done for a reason because WordPress didn’t have a simple way to add the new’ish async and defer attributes, but that is no longer the case.

Since Version 4.1 a new filter has been introduced that finally provides a less painful solution to add async or defer attributes without bastardizing the process.

apply_filters('script_loader_tag', string $tag, string $handle, string $src);


function add_async_attribute($tag, $handle) {
    if ( 'my-js-handle' !== $handle )
        return $tag;
    return str_replace( ' src', ' async="async" src', $tag );
add_filter('script_loader_tag', 'add_async_attribute', 10, 2);

If you want to use defer just replace async="async" with defer="defer".

Add this to your functions.php or equivalent file such as custom.php for Thesis.

You will need to change the handle which (if you enqueued your script correctly) will be the first parameter of the enqueue method.

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

How you choose which tag to use depends entirely on the nature of the script itself and there are 3 possible choices.

  • Standard – This is without the async or defer attribute and is the standard behavior for your browser.
  • Deferred – This delays the execution of the script until the HTML parser has finished.
  • Asynchronous – This executes when the script is ready and doesn’t disrupt HTML parsing.

As always you should test any changes you make and if you find that you have errors associated with either attribute then just don’t use them for that particular script.

What if you want to add the async or defer tag to multiple scripts?

Updated: 25/08/2015

In order to add the async/defer tag to multiple scripts we need to create an array and then loop through that array and add the async/defer tag to each script. Add either of these snippets to your themes functions.php or custom.php for Thesis 2 and edit the array to include your own script handles.


function add_defer_attribute($tag, $handle) {
   // add script handles to the array below
   $scripts_to_defer = array('my-js-handle', 'another-handle');
   foreach($scripts_to_defer as $defer_script) {
      if ($defer_script === $handle) {
         return str_replace(' src', ' defer="defer" src', $tag);
   return $tag;
add_filter('script_loader_tag', 'add_defer_attribute', 10, 2);


function add_async_attribute($tag, $handle) {
   // add script handles to the array below
   $scripts_to_async = array('my-js-handle', 'another-handle');
   foreach($scripts_to_async as $async_script) {
      if ($async_script === $handle) {
         return str_replace(' src', ' async="async" src', $tag);
   return $tag;
add_filter('script_loader_tag', 'add_async_attribute', 10, 2);

Let me know if you have any questions below.


In Category: WordPress

Matthew Horne

I am a web developer from the United kingdom who taught himself PHP and JavaScript and continues to build on those skills as well as learn new skills.

Show 38 Comments
  • Michael Darmanin 28th August 2015, 2:06 pm

    Cheers for going through the trouble of making this useful article. Saves me having to install a plugin.

    • Matthew Horne 29th August 2015, 4:35 am

      NO problem man, saves me time too!

  • manny 15th September 2015, 2:53 am

    very helpful. thank you!

  • Dave 22nd September 2015, 9:23 pm

    Hey Matthew,

    Thanks for this. Very helpful.

    One question: Let’s say I have 2 or 3 scripts that I ALL want to load asynchronously. Are arrays supported, and could you help me with the proper syntax? — I’d like to avoid writing a new function for each and every script.

    • Matthew Horne 30th September 2015, 2:18 pm

      Hi Dave, I believe you could use an array and a foreach loop within the add_defer_attribute function.

      I will update the post once i have tested it.

  • Brian 17th November 2015, 6:57 pm

    Hey Matthew – Thanks for putting this together! Definitely echo Dave’s sentiment, if you can get this to work with an array for multiple scripts I’d really appreciate it!

  • TheBerry 15th January 2016, 6:04 pm

    Thanks for this post. I have a similar question to Dave above. I tried this below but it only works on the first element in the array.

    //array of all functions that I want to be asynchronous
    $GLOBALS[‘script_array’] = [‘jquery-cookie’,’autoloadpost’];

    //add way to have async capabilities of script files
    add_filter(‘script_loader_tag’,’add_defer_attribute’, 10,2);

    function add_defer_attribute($tag,$handle){
    foreach ($GLOBALS[‘script_array’] as $individual_script) {
    echo “This is individual report script “,$individual_script;
    if($individual_script !== $handle){
    return $tag;
    return str_replace(‘src’, ‘async=”async” src’, $tag);

    • Matthew Horne 9th March 2016, 10:21 am

      I have updated the post to include a method for adding the async attribute to multiple scripts.

      • Nathan 23rd August 2016, 11:35 pm

        I’m still only seeing it work in the first item in the array, too?

        • Matthew Horne 25th August 2016, 4:31 am

          just updated it, should work now.

  • newbee 1st February 2016, 8:50 am

    Hey Matthew ,
    I am not understanding how to use this code you mentioned,Can Please elaborate , ( I am sure there must be many like me out there 🙂 ), GTmetrics given me below warning

    236.4KiB of JavaScript is parsed during initial page load. Defer parsing JavaScript to reduce blocking of page rendering.

    what should I do ?

    • Matthew Horne 9th March 2016, 10:11 am

      Not all scripts will be able to take advantage of async and defer tags. jQuery is one of them because too many scripts rely on it and not all developers properly enqueue scripts.

      Other scripts that might not benefit are adverts.

  • Ibrahim 18th March 2016, 3:05 pm

    How do I apply both async AND defer for a single script?

    • Josh 3rd June 2016, 5:10 pm

      You would not want to do this. Both do the same thing with one minor difference. Async will continue to parse and load the js file when it is done downloading. This means if you have multiple js files with async, they potentially can get loaded in a different order.
      Async Example – If file B finishes before file A it will get executed before file A. (This can be bad if B was dependent on code found in A).

      Defer corrects this. It still behaves like async but preserves the order.
      Defer Example – If file B finsihes downloading before file A it won’t get read/executed until file A has finished downloading, being read, and being executed.

      I simplified a tiny bit. Read more here

    • Josh Habdas 15th April 2017, 6:56 pm

      You actually may want to do this. According to the specs it’s up to the browser to determine how best to load an asset when both async and defer are supplied. If you’re supporting legacy versions of Internet Explorer they will defer the script to prevent blocking behavior and, for more modern browsers, they will load the script asynchronously. Try it!

  • Geno 26th March 2016, 3:21 pm

    First, thank you for this script! It seems to work with the javascripts that are in the wp-includes/js directory, but if they are not in that directory, the script doesn’t work. Do I use relative paths with wp-includes/js as starting directory?

    Thanks in advance for your answer.

    • Matthew Horne 28th March 2016, 3:49 am

      As long as you enqueue a script correctly eg. wp_register_script(‘my-js-handle’, ‘/path/to/my/script.js’, $deps, $ver, $in_footer);

      Then script.js would be added the to the array of scrips to add the async attribute to.

  • Piotr 12th May 2016, 10:16 pm

    For multiple scripts, instead of iterating use in_array function, much cleaner solution.

    • Matthew Horne 15th May 2016, 3:50 am

      Good point.

  • INDOMASCOT 16th June 2016, 6:55 pm

    Thanks friend,
    I follow your tutorial it to show google badge onto my website sidebar.
    And it works!


  • Asher Gomez 18th August 2016, 10:46 am

    The multiscript version doesn’t seem to work on wordpress 4.6

    • Matthew Horne 25th August 2016, 4:01 am

      I am going to do some testing now and see what the issue is.

  • Nilesh 23rd August 2016, 7:58 am

    For multiple script it is not working for me. It is adding defer tag only for the first element of the array.

    • Matthew Horne 25th August 2016, 4:01 am

      Ok, I will do some testing and see what is going on.

  • Venkatesh Bandari 27th September 2016, 10:07 am

    Hi Sir, I site is not loading async script Ex. google adsense ads. By adding this code on my site will that ads get show up, and where i need to ad this code in my site. Please guide me.

    • Matthew Horne 29th September 2016, 2:57 am

      Google adsense already has an async option when you configure your ads.

  • Dominic 5th October 2016, 7:48 am

    Thanks for this post. Long time reader here 🙂 . Many people do not have an idea that there is no need to use plugins for this 🙂

  • Anh Tran 14th October 2016, 3:48 am

    Nice technique. Just implemented on my website and it works nicely!

    Thanks for sharing.

  • Roee 23rd November 2016, 7:57 pm

    Very nice technique ! i’ll use it 🙂

  • Michael 29th November 2016, 4:47 pm

    I am a newbe. What should ‘my-js-handle’ be replaced with?

    • Matthew Horne 3rd December 2016, 3:05 am

      Hi Michael,

      developers should always register their scripts as follows:

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

      so you would replace my-js-handle with whatever the scripts registered handle is, the handle is just a unique identifier.

  • vinoth 20th December 2016, 4:54 pm

    Awesome man, Worked like charm!.

  • Josh Habdas 15th April 2017, 4:28 pm

    How does this differ from the universal method proposed on StackOverflow in ’13?

  • Josh Habdas 15th April 2017, 5:29 pm

    Never mind. I see it now. Would be nice to pay homage to the bastardization approach as it likely paved the way for the filtering stuff (and is quite clever IMHO). You can delete or keep these comments.

  • Josh Habdas 16th April 2017, 6:58 am

    Here’s what I ended up doing to tackle the jQuery performance issue:

Leave a Comment