• Feed RSS

How to Include JavaScript and CSS in Your WordPress Themes and Plugins

"Knowing the proper way to include JavaScript and CSS files in your WordPress themes and plugins is very important for designers and developers. If you don’t adhere to best practices, you run the risk of conflicting with other themes and plugins, and potentially creating problems that could have been easily avoided. This article is intended as a reference for playing nicely with others.


Best Practices Make Everyone Happy

If you’ve ever developed a theme or plugin for WordPress, or worked with one that someone else has created, you’ve probably come across several different methods for including JavaScript and CSS. While there are several methods that may appear to work in a specific set of circumstances, there is one primary method recommended in the WordPress Codex. This preferred way will ensure your theme or plugin works in all cases, assuming others also code the correct way.
There’s also some misunderstanding about what exactly the Codex says about this, which I will help clarify.

What’s in the Box?

When you download WordPress, a selection of common JavaScript libraries are already included that you can use for your JavaScript development. A list of included libraries can be found in the WordPress Codex wp_enqueue_script article.
All those libraries are included, but by default WordPress only loads the ones it needs to, and only when it needs them in the admin. If you write JavaScript that utilises one of these libraries, you need to tell WordPress that your script needs the library loaded first.

Telling WordPress About Your Script and What It Needs

Some of the things to think about when you’re coding JavaScript for WordPress are:
  • Is there an included library I can use?
  • Can I use the version that’s included?
  • Do I need to load my script in the front-end and in the admin?
  • Which front-end and admin pages do I need to load my script on?
Answering these questions helps you know what you need to do to register and load your script. This is done using a WordPress function called wp_register_script, and here is its usage according to the WordPress Codex:
wp_register_script( $handle, $src, $deps, $ver, $in_footer );
So what are these variables and do we need them every time? (This is covered on the Codex page, so I’ll be brief and use plain English)
  • $handle – what you’ll use to refer to this particular script wherever you might need to enqueue it, and you have to include this variable at the very least
  • $src – the path to the source file within your plugin or theme
  • $deps – an array containing the $handle for any other scripts your script needs to run (i.e. a dependency)
  • $ver – the version number for your script, which can be used for cache-busting. By default, WordPress will use its own version number as the version number for your script
  • $in_footer – do you want your script to load in the footer? Set this to true or false. It is false by default, so it loads in the header where wp_head() is, and if you specify true it will load where wp_footer() appears in the theme

What Is “Cache-Busting”?

Browsers remember what scripts and stylesheets they’ve downloaded for a particular site based on the URL of the script and stylesheet. If you change the URL, even just by adding a querystring, the browser assumes it’s a new file and downloads it.

Ok, So Let’s Try Some Examples

Here is the most basic example for loading a custom script:
function wptuts_scripts_basic()
{
 // Register the script like this for a plugin:
 wp_register_script( 'custom-script', plugins_url( '/js/custom-script.js', __FILE__ ) );
 // or
 // Register the script like this for a theme:
 wp_register_script( 'custom-script', get_template_directory_uri() . '/js/custom-script.js' );

 // For either a plugin or a theme, you can then enqueue the script:
 wp_enqueue_script( 'custom-script' );
}
add_action( 'wp_enqueue_scripts', 'wptuts_scripts_basic' );
First, we register the script, so WordPress knows what we’re talking about. The way to find the path for our JavaScript file is different whether we’re coding a plugin or a theme, so I’ve included examples of both above. Then we queue it up to be added into the HTML for the page when it’s generated, by default in the <head> where the wp_head() is in the theme.
The output we get from that basic example is:
<script type="text/javascript" src="http://yourdomain.com/wp-content/plugins/yourplugin/js/custom-script.js?ver=3.3.1"></script>
Now if your script relies on one of the libraries included with WordPress, like jQuery, you can make a very simple change to the code:
function wptuts_scripts_with_jquery()
{
 // Register the script like this for a plugin:
 wp_register_script( 'custom-script', plugins_url( '/js/custom-script.js', __FILE__ ), array( 'jquery' ) );
 // or
 // Register the script like this for a theme:
 wp_register_script( 'custom-script', get_template_directory_uri() . '/js/custom-script.js', array( 'jquery' ) );

 // For either a plugin or a theme, you can then enqueue the script:
 wp_enqueue_script( 'custom-script' );
}
add_action( 'wp_enqueue_scripts', 'wptuts_scripts_with_jquery' );
Note: By default, jQuery is loaded with noConflict to prevent clashes with other libraries (such as Prototype). See the noConflict section of the Codex if you don’t know how to deal with that.
See what I did there? You just add an array with the ‘jquery’ handle as a dependency. It uses an array here, because your script could have multiple dependencies. If your script uses jQuery and jQuery UI, you’d add jQuery UI to your dependency array, like array( 'jquery', 'jquery-ui-core' )
So now the output has changed, and we can see that jQuery has also been added into the <head> of the page:
<script type='text/javascript' src='http://yourdomain.com/wp-includes/js/jquery/jquery.js?ver=1.7.1'></script>
<script type='text/javascript' src='http://yourdomain.com/wp-content/plugins/yourplugin/js/custom-script.js?ver=3.3.1'></script>
Let’s try an example with all the bells and whistles:
function wptuts_scripts_with_the_lot()
{
 // Register the script like this for a plugin:
 wp_register_script( 'custom-script', plugins_url( '/js/custom-script.js', __FILE__ ), array( 'jquery', 'jquery-ui-core' ), '20120208', true );
 // or
 // Register the script like this for a theme:
 wp_register_script( 'custom-script', get_template_directory_uri() . '/js/custom-script.js', array( 'jquery', 'jquery-ui-core' ), '20120208', true );

 // For either a plugin or a theme, you can then enqueue the script:
 wp_enqueue_script( 'custom-script' );
}
add_action( 'wp_enqueue_scripts', 'wptuts_scripts_with_the_lot' );
Ok, so I’ve now added a version and specified that this script needs to be loaded in the footer. For the version number, I’ve chosen to use today’s date because it’s easy to keep track of, but you can use any version numbering you like. The output for this one is slightly different too, jQuery is output in the <head> and our script along with jQuery UI is output just before </body>, like this:
<head>
...
<script type='text/javascript' src='http://yourdomain.com/wp-includes/js/jquery/jquery.js?ver=1.7.1'></script>
...
</head>
<body>
...
<script type='text/javascript' src='http://yourdomain.com/wp-includes/js/jquery/ui/jquery.ui.core.min.js?ver=1.8.16'></script>
<script type='text/javascript' src='http://yourdomain.com/wp-content/plugins/yourplugin/js/custom-script.js?ver=20120208'></script>
</body>

Getting Your Priorities Straight

Some people may prefer not to use the proper enqueuing methods because they feel they have less control over the order in which scripts are loaded. For example, in a theme that uses modernizr, the theme author might want to make sure modernizr is loaded early on.
Something I haven’t mentioned earlier is more detail on how the add_action function works, as this is where we can exercise a little influence over the order of things. Here’s the usage of the function according to the WordPress Codex page:
add_action( $tag, $function_to_add, $priority, $accepted_args );
Note that often, and up until now in this article, only the $tag and $function_to_add parameters are used. The $priority parameter defaults to 10, and the $accepted_args parameter defaults to 1. If we want our scripts or styles to be enqueued earlier, we simply lower the value for $priority from the default. For example:
function wptuts_scripts_important()
{
 // Register the script like this for a plugin:
 wp_register_script( 'custom-script', plugins_url( '/js/custom-script.js', __FILE__ ) );
 // or
 // Register the script like this for a theme:
 wp_register_script( 'custom-script', get_template_directory_uri() . '/js/custom-script.js' );

 // For either a plugin or a theme, you can then enqueue the script:
 wp_enqueue_script( 'custom-script' );
}
add_action( 'wp_enqueue_scripts', 'wptuts_scripts_important', 5 );
The output will be the same as we’ve seen previously, but it will occur earlier in the HTML document.

Overriding Default Libraries and Using Content Delivery Networks

There may be times when you want to use a different version of a library that’s included with WordPress. Perhaps you want to use a cutting-edge version or you don’t want to wait for the next release of WordPress before using the latest stable version of jQuery. Another reason might be that you want to take advantage of Google’s CDN version of a library.
It’s important to note that this should only be done on plugins or themes used on sites that you will be personally maintaining. Any plugins or themes that you release for public use should use the libraries included with WordPress.
“Why?!”, I hear you ask. For the simple reason that you don’t control those sites. You don’t know what other plugins and themes might be used there, and you don’t know how often they will update your plugin or theme. Using the libraries packaged with WordPress is the safest option.
Having said that, if you are wanting to do this on a site you control, here’s how it’s done:
function wptuts_scripts_load_cdn()
{
 // Deregister the included library
 wp_deregister_script( 'jquery' );

 // Register the library again from Google's CDN
 wp_register_script( 'jquery', 'http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js', array(), null, false );

 // Register the script like this for a plugin:
 wp_register_script( 'custom-script', plugins_url( '/js/custom-script.js', __FILE__ ), array( 'jquery' ) );
 // or
 // Register the script like this for a theme:
 wp_register_script( 'custom-script', get_template_directory_uri() . '/js/custom-script.js', array( 'jquery' ) );

 // For either a plugin or a theme, you can then enqueue the script:
 wp_enqueue_script( 'custom-script' );
}
add_action( 'wp_enqueue_scripts', 'wptuts_scripts_load_cdn' );
So first of all, I deregister the included version of the library, otherwise conflicts between different versions could be introduced. Then register the alternate version, using the same handle, and I’ve chosen to specify null as the version (it’s already in the URL!) and specified not in the footer. The rest of our code is the same, because we were depending on whatever script used the ‘jquery’ handle. The output we get now looks like:
<script type='text/javascript' src='http://ajax.googleapis.com/ajax/libs/jquery/1.7.1/jquery.min.js'></script>
<script type='text/javascript' src='http://yourdomain.com/wp-content/plugins/yourplugin/js/custom-script.js?ver=3.3.1'></script>
Note: One of the reasons this is a bad idea to do in a plugin or theme for public release, is that all other plugins and themes used on this site will now have to use this version of jQuery. Also, the newly registered version of jQuery doesn’t have noConflict set, so if any other plugin or theme scripts use Prototype for example, this will break things.

Don’t Be Greedy

So far we haven’t mentioned anything about how to do all this in the admin, only on the front-end. The primary difference is what action to use. Instead of add_action( 'wp_enqueue_scripts', 'wptuts_scripts_basic' ); which we use for the front-end, the action for the admin is add_action( 'admin_enqueue_scripts', 'wptuts_scripts_basic' );
Something that’s important to do for both the front-end and admin is be selective about which pages you load your scripts on. If your plugin or theme has a script that only does something on one front-end or admin page, such as the theme’s options page, or maybe a page with a specific widget, you only need to load your script on that page. No point clogging things up and loading scripts on pages where they’re not being used!
There’s a great example in the WordPress Codex on how to load scripts only on plugin pages. Because plugins and themes can vary a lot in how they’re written, I won’t go into specifics here on how to be choosy about which pages you load scripts on, but it was important to mention so you’re aware of it when you’re coding.

That’s Scripts, Now Styles

The process for styles is almost exactly the same as the process for scripts. It is done using a WordPress function called wp_register_style, and here is its usage according to the WordPress Codex:
wp_register_style( $handle, $src, $deps, $ver, $media );
Note that the only difference there between wp_register_script and wp_register_style is that instead of an $in_footer parameter, we have a $media parameter. This parameter can be set to any of the following: 'all', 'screen', 'handheld', and 'print', or any other W3C recognised media type.
So an example of how you might enqueue a style would be:
function wptuts_styles_with_the_lot()
{
 // Register the style like this for a plugin:
 wp_register_style( 'custom-style', plugins_url( '/css/custom-style.css', __FILE__ ), array(), '20120208', 'all' );
 // or
 // Register the style like this for a theme:
 wp_register_style( 'custom-style', get_template_directory_uri() . '/css/custom-style.css', array(), '20120208', 'all' );

 // For either a plugin or a theme, you can then enqueue the style:
 wp_enqueue_style( 'custom-style' );
}
add_action( 'wp_enqueue_scripts', 'wptuts_styles_with_the_lot' );
This is a fairly comprehensive example, utilising most of the parameters, and the output it produces looks like:
<link rel='stylesheet' id='custom-style-css'  href='http://yourdomain.com/wp-content/plugins/yourplugin/css/custom-style.css?ver=20120208' type='text/css' media='all' />

So, Why Doesn’t Everyone Already Do Things This Way?

Good question, and the other question I guess you might ask is, “What makes you think this is the ‘right’ way and not just your preference?”. Essentially the answer is that this is the approach recommended by WordPress. It ensures that any combination of plugins and themes should be able to work together happily and without doubling up.
I’ve seen a few themes and frameworks around the place that use <script></script> and <link /> tags in their header.php, and even footer.php, files to load the scripts and styles for the theme itself. There’s really no reason to do things this way. As I’ve demonstrated above, it’s perfectly possible to prioritise scripts and styles and nominate whether they load in the header or footer from the comfort and safety of your functions.php. The benefit being that your theme / framework will work with a wider range of other plugins / child themes.
One example was loading jQuery using the <script></script> tags, which might appear to work nicely, but this can actually cause jQuery to be loaded twice! Loading jQuery in this way will not stop WordPress from loading its version of jQuery for other plugins, as WordPress’ version is in noConflict mode by default, and a plugin may specify it as a dependancy. So now you’ll have jQuery working for both noConflict mode and $, and also probably break any plugin that uses the Prototype library.

Conclusion

WordPress is a fantastic system, and it has been developed with a lot of thought. If there’s a mechanism made available to do something, it’s often a good idea to use it. When developing your plugins and themes, try to remember to code thoughtfully and for playing nicely with others.
What do you think about the use of wp_enqueue_script and its associated functions and actions? Do you know of any examples where it’s being done incorrectly? Do you know of any reason not to follow the advice above?"