Asset Loading in  WordPress

Simple and effective ways to load CSS and JS modules in a custom WordPress theme from scratch (no frameworks)

Learn the essentials for loading CSS and JS assets in WordPress, including auto-versioning, ES modules, and when minification actually matters (hint: it usually doesn’t). I’ll also share a quick trick for analyzing all scripts and styles loaded on your site. Let’s dive in.

Basic Loading

The starting point in WordPress for asset loading is using wp_enqueue_script() and wp_enqueue_style(). These functions handle JavaScript and CSS enqueuing, keeping things modular and avoiding the pitfalls of hardcoding resources in your theme.

Here’s a basic example to load a CSS and JavaScript file from a theme:

/**
 * Basic asset loading
 * Loads CSS and JavaScript files from a theme
 */
function my_theme_enqueue_assets() {
    wp_enqueue_style('my-theme-styles', get_template_directory_uri() . '/style.css');
    wp_enqueue_script('my-theme-scripts', get_template_directory_uri() . '/script.js', array(), null, true);
}
add_action('wp_enqueue_scripts', 'my_theme_enqueue_assets');

This is just the foundation. It’s modular, yes, but let’s make it smarter.

Auto-versioning

Caching can become a nightmare when you update CSS or JavaScript files, as users’ browsers often don’t get the latest versions immediately. One quick way to handle this is by setting a version number that changes automatically whenever the file updates. Using PHP’s filemtime() function, you can dynamically assign a version number based on the file’s last modified time. This handles cache-busting for you, automatically.

Here’s the previous example, now with auto-versioning:

/**
 * Autoversioned asset loading
 * Uses file modification time to auto-version assets
 */
function my_theme_enqueue_versioned_assets() {
    $style_version = filemtime(get_template_directory() . '/style.css');
    $script_version = filemtime(get_template_directory() . '/script.js');

    wp_enqueue_style('my-theme-styles', get_template_directory_uri() . '/style.css', array(), $style_version);
    wp_enqueue_script('my-theme-scripts', get_template_directory_uri() . '/script.js', array(), $script_version, true);
}
add_action('wp_enqueue_scripts', 'my_theme_enqueue_versioned_assets');

Now, each time your CSS or JS files change, the version does too. No manual updates needed.

ES Modules

WordPress 6.5 added native support for ES modules, allowing you to load JavaScript files as modules right out of the box. With modules, you can break code down into smaller parts, loading only what’s needed. It’s a solid way to cut down on load times and improve performance.

Here’s how to load a script as a module and set it as a dependency for another script:

/**
 * ES module loading
 * Loads a JavaScript file as an ES module with dependencies
 */
function my_theme_enqueue_module_assets() {
    wp_enqueue_script('my-module', get_template_directory_uri() . '/module.js', array(), null, true);
    wp_script_add_data('my-module', 'type', 'module');
    wp_enqueue_script('my-dependent-script', get_template_directory_uri() . '/dependent.js', array('my-module'), null, true);
}
add_action('wp_enqueue_scripts', 'my_theme_enqueue_module_assets');

This example loads my-module as an ES module, and then my-dependent-script depends on it. WordPress handles the dependencies for you, so only the files needed at that point get loaded.

JavaScript Modules and Performance

Why bother with ES modules? The answer is performance. By splitting JavaScript into smaller modules, the browser can load only the code it needs, improving load times and reducing the time your site spends processing JavaScript. This directly impacts metrics like LCP (Largest Contentful Paint), which Google looks at for SEO. Faster loading = better scores, which can mean better rankings.

Minification: Do You Need It?

The topic of minification comes up all the time, but here’s the reality: minification often doesn’t give you the big performance gain you’d expect when server-level compression is in place. To make the point, here’s a table from Privatenumber’s Minification Benchmarks:

Minifier JavaScript Minification CSS Minification
esbuild 68.23% 78.58%
Terser 68.50% N/A
CleanCSS N/A 79.03%

When files are served with Gzip or Brotli compression, the difference in file size with and without minification is minor. This is especially relevant if you’re using ES modules, which you can load directly from the file system without a minification step. That can save significant build time, leading to a faster workflow. You’ll still want to use Sass if you rely on it, but for many WordPress sites, skipping JavaScript minification may make sense.

If your build process feels slower than it should be, consider cutting out the minification step. For small to medium projects, this can be a smart way to reduce friction in your workflow.

Script Loading Analysis

Keeping track of all the scripts and styles loaded on a WordPress site can be tough, especially when multiple plugins are involved. Here’s a quick function you can use to analyze assets. This function outputs a table listing each enqueued asset’s name, source, and file size, and it’ll show up in a WordPress dashboard widget.

/**
 * Script loading analysis
 * Adds a dashboard widget listing all enqueued scripts and styles
 */
function my_theme_enqueue_analysis() {
    global $wp_scripts, $wp_styles;

    echo '<table><tr><th>Name</th><th>Source</th><th>Size</th></tr>';
    foreach ($wp_scripts->queue as $script) {
        $src = $wp_scripts->registered[$script]->src;
        $size = filesize(ABSPATH . str_replace(get_site_url(), '', $src));
        echo "<tr><td>{$script}</td><td>{$src}</td><td>{$size} bytes</td></tr>";
    }
    foreach ($wp_styles->queue as $style) {
        $src = $wp_styles->registered[$style]->src;
        $size = filesize(ABSPATH . str_replace(get_site_url(), '', $src));
        echo "<tr><td>{$style}</td><td>{$src}</td><td>{$size} bytes</td></tr>";
    }
    echo '</table>';
}

function add_analysis_dashboard_widget() {
    wp_add_dashboard_widget('asset_analysis', 'Asset Analysis', 'my_theme_enqueue_analysis');
}
add_action('wp_dashboard_setup', 'add_analysis_dashboard_widget');

This will add a widget called “Asset Analysis” to your WordPress dashboard. It’s a straightforward way to see what’s loading on the frontend, so you can spot bloat or redundancies quickly.

Conclusion

This post covered everything from the basics of asset loading in WordPress to more advanced concepts like auto-versioning and ES modules. We also touched on why minification isn’t always necessary and looked at a quick way to get insights into all enqueued scripts and styles. Each of these techniques can help streamline your workflow and improve your site’s performance.

If you’ve got questions or tips, feel free to reach out. Happy coding!