An Introduction to Asset Handling in WordPress
As a WordPress theme or plugin developer, there will come a time during your development process that you will need to include third party scripts or stylesheets in your plugin or theme. Sure, simply linking the assets in the header or footer would definitely work, but adhering to standard practice using the provided WordPress API will ensure that your theme or plugin will play nice with other plugins in the WordPress ecosystem.
If you want to provide maximum compatibility with other plugins and themes available, asset handling in WordPress is definitely a skill you should master.
This article is targeted to the beginner WordPress developer, with the assumption that you have a working knowledge of how WordPress action and filter hooks work.
The Basics
WordPress has provided a few basic functions to help the developer correctly load custom assets of their theme or plugin. The four main functions that you will use frequently are wp_register_script
, wp_enqueue_script
, wp_register_style
and wp_enqueue_style
.
Let’s go through these four major functions one by one.
wp_register_script( $handle, $src, $deps, $ver, $in_footer );
I’m just going to go ahead and paste the description provided in the codex for this function.
Registers a script file in WordPress to be linked to a page later, which safely handles the script dependencies.
Only $handle
and $src
arguments are required, while the latter three is optional.
$handle
is the name of the script and should be unique. This is the most important part when enqueuing your script as the $handle
will be the identifier of which script is to be loaded.
$src
is the URL to the script. You can get the proper URL using provided built in functions such as plugin_url()
, get_template_directory_uri()
and get_stylesheet_uri()
. For a remote URL, protocol-agnostic URL such as //ajax.googleapis.com/ajax/libs/jquery/2.1.3/jquery.min.js
can be used.
$deps
should be an array of scripts’ handle of which your script depends on. By defining them here, WordPress will load those dependencies first before loading your custom scripts.
$ver
is simply your script’s version, and it will be appended as a query string in your src
parameter of script
tag.
$in_footer
is a Boolean flag that will tell WordPress whether to load your script in head
section of document, or in the footer instead. Make sure that the theme properly includes wp_head()
and wp_footer()
correctly for this to work.
wp_enqueue_script( $handle, $src, $deps, $ver, $in_footer );
This function is similar to wp_register_script
with the exception that only $handle
parameter is required. If you have already registered your scripts prior to execution using wp_register_script
, you can directly enqueue them using wp_enqueue_script('your-registered-handle')
.
This is the actual function that will link your script to the generated page, so this function needs to be used in conjunction to wp_register_script
.
wp_register_style( $handle, $src, $deps, $ver, $media );
This is the counterpart function for wp_register_script
, only that this function will handle all your stylesheet registration. Let’s go through each of the arguments for this function.
$handle
is the unique identifier to your stylesheet, similar to the one for your wp_register_script
and wp_enqueue_script
. This is a required parameter and your handle should be unique to your WordPress installation.
$src
will be the URL to your CSS file. Again, you can either link it to the local file in your theme or plugin, and it also works with a remote URL.
$deps
will handle all your CSS’s dependency, which should be defined as an array of the styles handles.
$ver
is optional parameter where you can define your CSS version, and it will be appended to the URL as the query string as well.
$media
is the CSS media types of your CSS. The complete list of acceptable value can be found on this page.
wp_enqueue_style( $handle, $src, $deps, $ver, $media );
This function will link your CSS to the generated page, similar to how wp_enqueue_script
works. So basically, you can either enqueue your previously registered CSS, or you can enqueue the CSS on the go by defining the $src
parameter as well.
When Do We Enqueue Them?
There are three proper hooks recommended to enqueue your scripts and stylesheets.
Use wp_enqueue_scripts
action hook whenever you want to enqueue your script to the front end of your site. Don’t be confused with the name, as the same hook can be used to enqueue your style as well.
How about the admin page? Well, you can hook your enqueue function on the admin_enqueue_scripts
and all your scripts and styles will be loaded on the administrative side of things correctly. To load your scripts and styles specifically on the login page, login_enqueue_scripts
action hook is provided.
Enqueue Versus Register
At a glance, it seems redundant to register your scripts and stylesheets first, before enqueuing them. So why do we need to use wp_register_script
and wp_register_style
in the first place, when in reality we can directly just use wp_enqueue_script
and wp_enqueue_style
instead? Well, technically, you don’t need to.
But if you want to dynamically load your scripts and stylesheets based on various conditions, you better register them first so you can enqueue them anytime you want at any point of execution without repeating yourself.
Let’s look at this simple example:
wp_register_script( 'my-custom-js', ... );
wp_register_script( 'my-second-js', ... );
if ( is_page( 32 ) ) {
wp_enqueue_script('my-custom-js');
}
if ( $var ) {
wp_enqueue_script('my-second-js');
}
Based on this snippet, you can see that we first register two custom scripts, my-custom-js
and my-second-js
. Based on several conditions, in this case, when the page loaded is of ID 32, or $var
is set to true
, we can dynamically enqueue them.
Registering them first is also useful whenever you want to rely on the same script on a different part of the website. As pointed out earlier, there are two different hooks that need to be used in order to load your scripts and stylesheets on front end facing side and administrative side. So by registering them first, you would just need to register them once, and enqueue them in both related hooks.
What’s Already Included?
Many developers resort to including a third party library of their own, which then leads to big plugin or theme sizes and the potential for incompatibilities. WordPress includes some useful JavaScript libraries of its own, so why not utilize that instead?
An exhaustive list of what’s already included with its respective handle is listed on this page under Handles and Their Script Paths Registered by WordPress section. This list may get outdated, so if you want to peek directly at the source, take a look at wp-includes/script-loader.php
source code, specifically inside the wp_default_scripts
function.
You can find a lot of useful libraries that can be reused without the need of shipping it inclusively with your plugin or theme.
Additional Functionalities
Besides the four main functions to register and enqueue your scripts and styles, WordPress also provides a handful of functions that can help with regards to managing your plugin or theme assets. Let’s take a look at some of them.
Making Your PHP Variables Available on the Client Side with wp_localize_script
Although this function is primarily used to provide proper localized translation of strings (thus the name) to your client side code, it can be used virtually to make any data that you have on the server side available on the client side as well.
wp_localize_script( $handle, $name, $data );
As the above code suggests, this function will accept three required parameters. $handle
refers to the handle of the script you are attaching the data for. It is important to note that the script must be either registered or enqueued first to make sure wp_localize_script
works correctly.
$name
is the variable name that will be available on the client side, and will contain all your specified $data
that you defined in an array.
Here’s a simple snippet to explain what this function does:
wp_register_script( 'my-custom-js', ... );
$args = array(
'foo' => 'bar'
);
wp_localize_script( 'my-custom-js', 'my_var', $args );
wp_enqueue_script( 'my-custom-js' );
When the my-custom-js
is enqueued, WordPress will handle the given data to make it available on the client side code. Since the $args
will be JSON-encoded and assigned to the variable called my_var
, you can always access the value back in your script like you normally do on any JavaScript object.
<script>
console.log( my_var.foo ); // print out "bar" in console
</script>
Enqueuing All Related Scripts for WordPress Media Library with wp_enqueue_media
If you want to use the native WordPress media uploader in your plugin or theme, you will need to enqueue all the related assets required before you can access all the available related APIs. To make things easier, WordPress provides a helper function that will help you enqueue all the related scripts, styles and templates related to the Media API.
wp_enqueue_media( $args );
The only parameter is $args
, which is optional, and can be used if you want to enqueue all related assets associated with a specific post. I won’t delve too much into how to use the available API to implement the native media uploader in your plugin or theme since it deserves an article on its own, but here’s a little snippet to get you started.
wp_enqueue_media(); // basic usage, enqueue all related scripts, styles and templates
wp_enqueue_media( array( 'post' => 32 ) ); // enqueue all related assets for post with ID of 32
Adding Metadata to Stylesheet with wp_style_add_data
This function works only for the stylesheet, and is particularly useful when you want to add metadata such as conditional comments, RTL support and an alternate title to your custom stylesheet.
wp_style_add_data( $handle, $key, $value );
All three parameters are required. Let’s go through each of them to see what they represent.
$handle
is the specific stylesheet handle that we want to add our metadata to.
$key
is the data point. There are five accepted values which are conditional
, rtl
, suffix
, alt
and title
$value
is the data string in conjunction to the previously specified $key
.
Here’s a example snippet on how we can take advantage of this function to load our stylesheet specific to Internet Explorer only.
wp_enqueue_style( 'custom-ie-style', get_template_directory_uri() . '/css/ie.css' );
wp_style_add_data( 'custom-ie-style', 'conditional', 'IE' );
That’s it! Once your stylesheet is linked to the generated page, this is what is actually printed on the head
part of the document.
<!--[if IE]>
<link rel="stylesheet" id="custom-ie-style-css" href="http://www.example.com/wp-content/themes/example/css/ie.css" type="text/css" media="all">
<![endif]-->
Automatically Cache Busting Using filemtime
This is not actually WordPress specific, but I kept finding myself using this code time and again in my client projects. What it actually does is take advantage of the version argument in the wp_enqueue_script
and related functions as a cache busting method by checking the file last modified time. Obviously, this only works for the local file, since we are using filemtime
PHP function to detect the last modified timestamp of that file.
Here’s how we define the version argument, in example of wp_enqueue_script
function:
wp_enqueue_script( 'my-custom-js', get_template_directory_uri() . '/js/custom.js', array(), get_template_directory() . 'js/custom.js' );
Note that filemtime
requires the path of your file, not the URL, thus in above snippet, we are using get_template_directory
function instead to get the path to our theme directory.
From now onwards, any modifications to the file will result in different version value, and our query string will get updated accordingly.
Conclusion
As a responsible WordPress developer, it’s vital that you to apply best practices to your development process, and that includes learning how to properly load assets in your custom plugin or theme. This will ensure your plugin or theme is compatible with other plugins or themes, as well as reducing conflicts and bugs as WordPress evolves.