Dear theme developers, this is how you add scripts in WordPress themes

59 Comments

properly enqueue scripts in WordPress  themes
There are a lot of themes out there. And a ton of them are not including javascript files properly. So theme developers, please pay attention: this is how to include scripts in your themes properly. And guess what? It’s really easy.

Let’s lay some groundwork:

  1. This example would go in your functions.php file.
  2. This example assumes your theme is a parent theme.

In this example, I’m registering four scripts, and enqueuing two. I’ll explain it afterward.

Now let’s break it down

We are using an action hook called wp_enqueue_scripts(). It’s your best friend. This hook ensures that your function is registering and enqueuing scripts in the right place and only on the front end.

We’ve prefixed our function. Because, you need to prefix all the things. Use a prefix that makes sense, but isn’t “wp” or something else that could be used by core or other plugins. Your full initials, sitename, etc. are good possibilities.

As a sidenote, I recommend adding the action in your own theme setup function.

We’re registering the scripts with wp_register_script(). Read that page. Get in the practice of providing every parameter of the function every single time you register a script.

  1. Handle – A name this script will go by. This is how you will enqueue it the short way, or if your user has a child theme and doesn’t want your script, they can dequeue it with this handle.
  2. Source – The path to the file. I used get_template_directory_uri(), which is a WordPress function that finds the parent theme directory. Never, ever, hard code the path with /wp-content or anything else. Use this function. It works. It’s great. If you are registering your script in a child theme, use get_stylesheet_directory_uri() instead. It’s the same thing, for child themes.
  3. Dependencies – These are the scripts your script depends on. You may recognize my scripts I’m loading. They’re two different slider scripts. They both depend on jQuery. You can pass multiple dependencies in this array, if necessary. For the script called “info-carousel-instance”, it relies on the “info-caroufredsel” script. Therefore when I enqueue “info-carousel-instance”, WordPress will know it needs to enqueue “info-caroufredsel” as well, if it hasn’t been enqueued already.
  4. Version – The version of the script you are loading. If you update your script, you can bump the version, and when you change the version here, WordPress will be sure to load the new one, and reset any caching that’s been used on the previous version of your script.
  5. Load in footer – If you set this to true, your script will be loaded in the footer, and therefore after much of the rest of the page has loaded. I always do this for things like sliders, because that way my slider script won’t prevent more important things on my page from loading. Just think about what you’re loading when you consider whether or not to do this.

wp_enqueue_script()

wp_enqueue_script() will enqueue scripts.

Definition: to place something into a queue; to add an element to the tail of a queue (line).

This function simply puts the script it is enqueueing in line to be loaded onto the page. WordPress is also smart enough to know if a script you are trying to enqueue has already “been through the line”, if you will, and won’t load the same scripts more than once. Whenever you see a website loading jQuery mutliple times, you now know why – they aren’t enqueueing, or at least not properly.

I’m enqueueing two of my four scripts. My carousel instance and the home page flex slider instance. The carousel will be loaded on every page, and the flex slider only on the homepage, as that’s the only place it’s used. If you don’t need a script on every page, don’t load it on every page. You can use conditional tags inside the wpcandy_load_javascript_files() function, around the instances of wp_enqueue_script() that are to be loaded conditionally. Automattic’s _S theme also has good examples of loading scripts with conditionals (like a script for comments – a common one to load this way).

wp_enqueue_script() takes the same arguments as wp_register_script(), but if you register your scripts first, you can just call them by the handle in wp_enqueue_script, as my example above shows.

As of WordPress 3.3, you can even enqueue scripts in specific page templates. It will load the script in the footer automatically! For instance, I could have enqueued the “home-page-main-flex-slider” script directly in the template part with the slider code.

Register vs. Enqueue

In our function, we’re registering four scripts, and enqueing two. Registering a script makes it available for use. Enqueuing actually pulls the script to the theme. You can register without enqueuing. But to load the script on the page, you need to enqueue. If you register the script as I show, you can then enqueue it by its handle alone. If you don’t register it ahead of time, you’ll need to provide the full parameters in the enqueue function.

The reason I register all of my scripts in this function is simple: it helps me keep track. Sure, I could just enqueue them all in this function with conditionals, but sometimes conditionals get confusing, and I like to take advantage of the ability to enqueue in templates, because it’s simple. I could also skip the register function for the scripts I enqueue right away, but again, I do it for organization. I register them there, together, so that I know what I’ve got and I know what I’m loading on every page versus in specific places. I also tend to make notes in comments by the register function to note where I’m enqueuing, if not immediately.

Notes

To load scripts in plugins, plugins_url() will replace get_template_directory_uri() for the path, and your function would be in your plugin file instead of functions.php. To load them in the admin, you’d use the admin_enqueue_scripts() function instead of wp_enqueue_scripts().

But this tutorial is just to show you how to load scripts in themes – meant for the frontend. Please start doing this. If you aren’t, you are doing yourself and your customers (whether buyers of themes or clients) a disservice and certainly causing future headaches.

Also, you’ll see I used the version of jQuery included in WordPress. So should you. You’re making a theme. Use the jQuery included with WordPress. It will always be up to date and compatible with core. It will always work. I’ve seen Otto argue with enough people about this (including me). He’s right. If you think Google’s jQuery is better, use this plugin, or recommend it to your users. For themes you release, just use the WordPress bundled version.

I hope this has helped clear things up for some of you. I’m not the first person to write about it. But maybe I helped it click for some. And as Carl Hancock said (he deals with conflicts daily), “it needs to be drilled into them [me, you, all of us]. So the more articles the better.” Oh, and if I screwed something up, or you have something to add, please let me know.

59 thoughts on “Dear theme developers, this is how you add scripts in WordPress themes

  1. Why are you enqueueing jQuery? You don’t need to. It’s a dependency of info-caroufredsel, which is a dependency of info-carousel-instance, which you’re enqueueing. jQuery will automagically be enqueued.

    I think you messed your example up slightly and you meant to enqueue home-page-main-flex-slider instead.

    • Thanks Otto, I revised it. Made the flexslider script conditional to the homepage, and took out jQuery. I have to admit, I do usually keep jQuery in despite the dependencies. I know it’s dumb, I guess it’s previously just given me peace of mind :)

  2. I’m more worried about how themes and plugins work with scripts and styles inside the admin panel. People often ask why their dashboard is so slow, right? Well, it’s because many themes and plugins will just include their sh stuff at admin_enqueue_scripts and yell out “worksforme,” while breaking and conflicting a dozen other plugins :)

    • +1 this.

      I think the enqueue process is one of the least understood aspects of WordPress theme development.

      Just yesterday I ran into a fancybox conflict problem where fancybox was being enqueued in the theme correctly but it an enqueue was also called within WooCommerce.

      I managed to solve in temporarily by removing the enqueue from the theme as it wasn’t actually being used.

      Is there a way of only firing enqueue requests when specific plugin pages are requested?

      • Hey Ed, of course there is, plus, nobody said you can’t use conditional tags. You can always compare to the $pagenow global, a get_query_var and even $_REQUEST entries. All that can help you target specific pages within the admin area, before firing your enqueue routine.

      • Remember, any one of the add_*_page functions will return the plugin pagehook on success, which you can then use to target your plugin page specifically, like so:

        add_action( 'load-' . $mypluginpagehook, 'my_init_function' );

        You can then add an action to admin_enqueue_scripts inside the my_init_function to ensure that your scripts, styles and any other files you load will only be loaded for your plugin page.

        For more general post types targeting and such, you can use get_current_screen or the $current_screen global to target specific post type pages, like so:

        /** For any MY-POST-TYPE screen */
        global $current_screen;
        if ( ‘MY-POST-TYPE’ == $current_screen->post_type ) {
        // Do stuff here
        }

        /** For the add/edit MY-POST-TYPE screens only */
        global $current_screen;
        if ( ‘MY-POST-TYPE’ == $current_screen->post_type && ‘post’ == $current_screen->base ) {
        // Do stuff here
        }

        Those little snippets can effectively keep your plugin junk from interfering with other plugin junk.

    • Yes I agree. If your admin scripts only need to work on one admin page (e.g. a Plugin/theme options page) then target that specific page ONLY! :)

  3. cheers Konstantin,

    I have a few conditionals already but didn’t realise I could use the $pagenow global and $_REQUEST – nice – thanks. *off to experiment*

    • Killer. Even if it only helps one or two developers realize they could improve their code, a post like this isn’t a waste of time.

      Of course, I’m more than willing to risk wasting Brian’s time. Zing!

      • Count me in for one of the (hopefully more than two) developers this post has helped. Bookmarked and will be referencing for future builds. Nice work!

  4. good post, i really cant stand when plugin and theme developers echo script tags to wp_head, 90% of the time it causes conflicts with other plugins.

  5. I want to add to this conversation regarding jQuery library usage and loading it via an external API.

    Deregistering jQuery and then registering and enqueuing it via Google’s API (or jQuery’s API) seems to be very popular with commercial theme developers.

    PLEASE DO NOT DO THIS.

    But calling jQuery via Google’s API should perform better shouldn’t it? Sure, it can. BUT it’s not appropriate for you to apply this to a theme you are distributing because it changes the WordPress environment without the users knowledge and in a way that can break jQuery functionality in plugins.

    How can it break things? Easy, when you do this you load a specific version of jQuery. The problem with this is that means its out of synch with what WordPress is using and what plugins are going to expect to be available.

    If your theme deregisters jQuery from WordPress and then you register and enqueue jQuery v1.7.1 via Google’s API in the theme then what happens when WordPress updates to jQuery v1.8? Potentially problems, that what will happen.

    A good example of this is WordPress introduced jQuery v1.6.1 and WordPress v3.3 introduced jQuery v1.7.1. So WordPress stays up to date with jQuery as updates are released.

    Functionality in jQuery v1.7.1 may not be compatible withy functionality in jQuery v1.8 or other future updates just like functionality in jQuery v1.7.1 is not compatible with older versions of jQuery.

    Let me give you a good example of why you shouldn’t do this as a theme developer…

    1. User purchased and installs your theme.
    2. Your theme deregisters jQuery in WordPress and registers it and loads v1.4.1 from Google’s API.
    3. User installs a plugin. Plugin requires WordPress v3.3 which uses jQuery v1.7.1.
    4. User is running WordPress v3.3 do this shouldn’t be an issue.
    5. Plugin doesn’t work properly due to JavaScript errors caused by the fact jQuery v1.4.1 is being loaded despite the fact that version of WordPress should be loading jQuery v1.7.1 because of your theme.
    6. User complains about the plugin and gets support from plugin developer.
    7. The end result is the plugin is broken because the theme is fundamentally changing the default WordPress environment which changes expected and standard behavior. Plugins need to rely on WordPress behaving exactly as expected and when your theme changes default WordPss behavior this can cause problems.

      The sad part is the plugin developer is the one that gets screwed in this scenario because the user blames the plugin and requests support from the plugin developer for an issue at is ultimately being caused by poor theme development practices and the fix is to fix the theme. So they end up supporting the theme developers user. Not very fair IMO.

      The solution to this issue is simple. Don’t do it. Use the jQuery that comes with WordPress.

      If a user is savvy enough and understands what they are doing they can implement this jQuery customization themselves. OR it can be handled by a plugin that stays up to date wand in synch with WordPress do these situations do not occur.

      Just say no to loading dregistring and loading jquery via Google or jQuery’s API in commercial themes.

      Excuse the typos… I typed this all on my iPad. So it won’t surprise me if numerous errors exist.

    • THANK YOU!
      Thank you for writing out everything that I have wanted to scream for so long. As a plugin developer, I cannot even come close to counting the number of support requests I’ve had because of a theme that decided to load jQuery from Google.

      Don’t do it!

      • Hey Japh, how about getting this implemented as a requirement for Themeforest WordPress themes? We run across lots of them that do this. Require themes use the built in jQuery and not deregister it or load another one.

    • Totally agree with Carl! I’ve had headaches solving customer problems because of JavaScript conflicts, and the problem is commonly caused by using outdated jQuery enqueued from Google.

    • Your main argument seems to be theme developers shouldn’t include their own version of jQuery because it can break core WordPress functionality. What if a theme developer uses jQuery’s built-in noConflict() function to isolate their included version? Then they can use whatever version of jQuery they want, as long as they rely on the var set for the noConflict():

      var $myj = jQuery.noConflict();

      Now as long as the designer uses $myj instead of $, all of $myj requests will be routed through jQuery 1.6.2.

      • You just gave me a good laugh. A lot of theme developers have no clue how to do what you described because they are too busy copy-n-pasting all the jQuery they use from other themes and jQuery plugins. They don’t know enough about it to implement it correctly, let alone in no conflict mode.

      • I wanted to add that my main argument has nothing to do with including your own version of jQuery (although that is unnecessary), it has to do with deregistering the built in jQuery and replacing it with a different version from somewhere else so that when a plugin enqueues jQuery using the WordPress enqueue function… the expected version of jQuery is not enqueued as a result.

        If WordPress v3.3 has jQuery v1.7.1 built in and my plugin uses the WordPress enqueue function to load jQuery and jQuery v1.7.1 is not what is returned… that’s a problem. Themes should not be changing the overall WordPress environments like this. It’s a major contributor to theme and plugin conflicts caused by JavaScript errors.

        It’s such a simple thing that it is so frustrating that so many theme developers can’t seem to get it right.

        The main problem is a lot of theme developers don’t really know what they are doing. They see another theme developer do it, or read a how to and assume that it must be a good way to do it… or they simply copy-n-paste from another theme.

        Developers who fall into this category have no business selling themes and unfortunately we run into them everyday supporting our WordPress plugin.

        Our biggest support drain has nothing to do with problems with our code, it has everything to do with fixing problems in poorly developed themes that cause JavaScript errors due to improper jQuery usage.

        • If I could go back in a time and NOT receive every support ticket I’ve gotten related to theme devs replacing jQuery, I would have so many hours of my life back.

    • I think it’s also VERY important to mention that the version of jQuery included with WordPress is noconflict while Google’s is not! Theme developers who deregister the included noconflict jQuery and included Google’s will automatically cause problems with ANY plugin that is expecting the noconflict version – because that’s what we should be working with.

      When theme developers do this, they are just being lazy, inconsiderate and/or ignorant! I’m dealing with this as I write this…

    • With WordPress 3.6 and JQuery 2.0 on the way – any idea how this will affect WP, and what this will mean for plugins, (two libraries?)

  6. Kudos! The more discussion the better. And in education, training, and all things learning, repetition is key. And frankly most of our education is being reminded of stuff we already know and building upon it ever so slightly.

    Also, a self-taught person may not understand the other person/people, and I believe you spell this out nicely, with a great many people here adding their thoughts makes this post valuable!

    So writing this is great! And though you knew this, Otto taught you something already and likewise many others.

    • Thanks, Travis. And yeah, Otto has taught me many things over the years. It’s only right he schools me in a post where my intent is to school others. It’s a natural chain of learning. : )

  7. Pingback: Another look at loading jQuery… yes, again! | ZS Labs

  8. I am not very good at code and your article has done me a favor. I am not saying I am able to develop a theme now it definitely encouraged me to try again my dead project.

    Thank you for sharing sir.

  9. Pingback: Loading Scripts Correctly in the WordPress Admin | Pippins Plugins

  10. Pingback: WordCamp NYC session slides and code | Josh Leuze

  11. I have some advice for frustrated wordpress scripters. If you’re having problems getting the scripts to include properly, go through
    this checklist and eliminate these common mistakes:

    1. Incorrect path – When uploading over ftp make sure you have the correct path and directory. It’s very common to switch directories and forget then make changes to a script and upload it to the wrong directory.

    2. When registering and enqueueing a jquery javascript file in a wordpress template, make sure the jquery is not in the normal format:

    $(document).ready( function (){
    //code
    });

    but instead:

    jQuery(document).ready( function ($){
    //code can now include the normal jquery format i.e. $("a")
    });
    //make sure the $ is passed in the .ready( function($) {

    3. It’s not enough to simply use wp_enqueue_script, you have to first use wp_register_script. Don’t forget to use add_action.

    4. If you run in to a brick wall and it’s not working go through a check list of common causes of error, and make sure it’s not a path problem.

  12. Hi, I’ve read this article, and trying to know more about the theme setup function, arrived HERE, where says that isn’t a good thing to load it there.
    I suspect that is because here you’re embebing the enqueue functions inside the ‘wp_enqueue_scripts’, and that inside the setup funtion, but I’m not sure. Can you clarify me that please?

    Thanks!

  13. Thanks so much, this post is very helpful. Can you tell me what you would do with an initialization script, like some initializaiton script

    I currently have it like this add_action(‘wp_head’,'some_script’), where some_script echos the script. Is that the right way?

  14. I think there is an important point which you might have missed here. Sometimes you can’t avoid these situations. Here’s an example:
    When you develop a theme based on a CSS framework, lets say bootstrap. You need to include Bootstrap.css before your style.css.
    Now,
    -> if you use @import bootstrap.css in your style.css, it is a bad practice and impacts page speed.
    -> If you add the bootstrap.css directly in the head using get_template_directory()…/css/bootstrap.css…which is again not recommended.
    -> If you enqueue bootstrap.css and style.css [S] in the wp_head hook leaving the main stylesheet blank. Now you face another issue: when user adds some custom css into the theme stylesheet it gets overwritten by the [S] stye.css.
    ….so sometimes it may become necessary to add files the “not recommended” way.
    Another glaring example of “Not recommended way” is the WP Theme customizer:
    If you have to use theme customizer in your theme then you have to add inline CSS to the page, it is again not recommended by Page speed standards. It adds extra bloat and the CSS can not cached or minified…
    There are a few more examples where WordPress doesn’t follow the recommended way.
    The whole point of above argument is that WP also needs to evolve and understand theme authors requirements…

  15. Hi,
    if a javascript file needs to be initialized by another script call, how do i enqueue the javascript call in addition to the main js file?

    Your suggestion shall be well appreciated.

    Thanks.

Leave a Reply

Please note that WPCandy is a moderated community.

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

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>