DRYing out WordPress Asset Enqueuing
WordPress has several handy functions for granular control over external assets. Specifically wp_enqueue_script()
, wp_register_script()
, wp_enqueue_style()
, wp_register_style()
. These functions allow us to register assets so they’re available and can be added to the page where applicable.
While these functions give a fair amount of control, if you have a complex site you can end up repeating the same four functions over and over again. This not only contributes to visual clutter it increases the likelihood of making a mistake like a misplaced argument or typo.
Realizing this at 3.7 DESIGNS, we started looking for a better way to handle asset registering and enqueuing. What we came up with is as follows.
Define once
Rather than repeating wp_register_
and wp_enqueue_
for each individual asset, we decided defining all the assets once via a multidimensional array would cut down on needless repetition and code bloat.
Each asset is defined using a nested array with keys for the relevant arguments passed into wp_enqueue__
and wp_register_
. Let’s look and see what’s required to register a script.
wp_register_script( string $handle, string $src, array $deps = array(), string|bool|null $ver = false, bool $in_footer = false );
We need a handle, file path, an array of dependencies, an optional version number, and whether it should show up in the footer or not.
So our scripts array could look something like this:
apply_filters( 'threeseven_global_scripts', $global_scripts = array( 'global' => array( 'handle' => 'global', 'uri' => get_template_directory_uri() . '/dist/js/global.min.js', 'deps' => array( 'jquery' ), 'ver' => THEME_VER, 'footer' => true, ), 'vendor' => array( 'handle' => 'vendor', 'uri' => get_template_directory_uri() . '/dist/js/vendor.min.js', 'deps' => array(), 'ver' => THEME_VER, 'footer' => true, ), 'customizer' => array( 'handle' => 'propagate_customizer', 'uri' => get_template_directory_uri() . '/dist/js/customizer.js', 'deps' => array( 'customize-preview' ), 'ver' => THEME_VER, 'footer' => true, 'condition' => is_customize_preview(), ), 'comment-reply' => array( 'handle' => 'comment-reply', 'condition' => is_singular() && comments_open() && get_option( 'thread_comments' ), 'register' => false ), ) );
Each asset has keys that correspond to arguments passed into the register function. You may have noticed we’ve added the additional keys of condition
and register
. We’ll use these to determine if the script needs to be registered and where it should be enqueued. More on this later.
Now we probably want to set some defaults so we don’t have to add unnecessary keys to our array. For example, most scripts will be registered before being enqueued. We can store those defaults in a separate array:
$default = array( 'condition' => true, 'register' => true, 'footer' => false, );
Here we’re setting that scripts should be enqueued, registered and included in the HEAD
by default.
Now that we’ve defined our defaults and scripts, we need to parse the array.
Parse once
Our variables are then passed into a function that will parse the array.
Our parse script looks like this:
three_seven_enqueue_scripts( $global_scripts , $default); function three_seven_enqueue_scripts( $scripts , $default ) { foreach( $scripts as $script ) { $script = array_merge( $default , $script); if( $script['register'] !== false ){ // Register the script so we can conditionally use it elsewhere wp_register_script( $script[ 'handle' ], $script[ 'uri' ], $script[ 'deps' ], $script[ 'ver' ], $script[ 'footer' ] ); } if( $script['condition'] && $script['print'] !== false ){ // These are global, so enqueue wp_enqueue_script( $script[ 'handle' ] ); } } }
First we merge each script with our $default
array. Because the $script
array is passed into array_merge
second, the $script
array will have priority on duplicate keys.
This is also where our register
and condition
keys come into play.
The register
key indicates if the script needs to be registered (duh.) This allows us to use scripts that are bundled and already registered through WordPress like jQuery UI. The condition
key allows us to dictate where the script should be loaded.
You probably already do this by putting if statements in your asset enqueue function, limiting assets to specific situations like only enqueuing on the homepage. If you look at our array, you’ll notice we’re essentially using the same approach, just streamlining it.
'condition' => is_singular() && comments_open() && get_option( 'thread_comments' ),
In the case of the comment-reply
script, we only want to enqueue it on single posts that have comments open if threaded comments are enabled. When all three conditions are met, this will return true and the script will be enqueued.
And we’re done! This is our first iteration and I’m sure there are ways to improve it (we’d love to hear your suggestions!). The result is cleaner code, less repetition. and fewer chances for simple mistakes.
Here is the entire code for those interested:
function threeseven_theme_assets() { define( THEME_VER, '1.0' ); // Setup global scripts wp_enqueue_script( 'jquery' ); $default = array( 'condition' => true, 'register' => true, 'print' => true, ); apply_filters( 'threeseven_global_scripts', $global_scripts = array( 'global' => array( 'handle' => 'global', 'uri' => get_template_directory_uri() . '/dist/js/global.min.js', 'deps' => array( 'jquery' ), 'ver' => THEME_VER, 'footer' => true, ), 'vendor' => array( 'handle' => 'vendor', 'uri' => get_template_directory_uri() . '/dist/js/vendor.min.js', 'deps' => array(), 'ver' => THEME_VER, 'footer' => true, ), 'customizer' => array( 'handle' => 'propagate_customizer', 'uri' => get_template_directory_uri() . '/dist/js/customizer.js', 'deps' => array( 'customize-preview' ), 'ver' => THEME_VER, 'footer' => true, 'condition' => is_customize_preview(), ), 'comment-reply' => array( 'handle' => 'comment-reply', 'condition' => is_singular() && comments_open() && get_option( 'thread_comments' ), 'register' => false ), ) ); three_seven_enqueue_scripts( $global_scripts , $default); // Setup global styles apply_filters( 'threeseven_global_styles', $global_styles = array( 'global' => array( 'handle' => 'global', 'uri' => get_stylesheet_directory_uri() . '/dist/css/style.min.css', 'ver' => THEME_VER, ), ) ); three_seven_enqueue_styles( $global_styles , $default ); } add_action( 'wp_enqueue_scripts', 'threeseven_theme_assets', 0 ); function three_seven_enqueue_scripts( $scripts , $default ) { foreach( $scripts as $script ) { $script = array_merge( $default , $script); if( $script['register'] !== false ){ // Register the script so we can conditionally use it elsewhere wp_register_script( $script[ 'handle' ], $script[ 'uri' ], $script[ 'deps' ], $script[ 'ver' ], $script[ 'footer' ] ); } if( $script['condition'] && $script['print'] !== false ){ // These are global, so enqueue wp_enqueue_script( $script[ 'handle' ] ); } } } function three_seven_enqueue_styles( $styles , $default ) { foreach( $styles as $style ) { $style = array_merge( $default , $style); if( $style['register'] !== false ){ // Register the stle so we can conditionally load / unload wp_register_style( $style[ 'handle' ], $style[ 'uri' ], $style[ 'ver' ] ); } if( $style['condition'] && $style['print'] !== false){ // These are global so enqueue wp_enqueue_style( $style[ 'handle' ] ); } } }