Compiling and Enqueuing Assets in WordPress with @wordpress/scripts

Dealing with WebPack is no picnic, and the problem only worsens the bigger or more complicated your project gets. Say you want to add SCSS compiling, or TypeScript, or SVG/imports imports in your CSS, or CSS modules — the list goes on — now you’ve not only got a ton of things you have to configure properly, you have a bunch of npm dependencies to manage.

What if I told you that @wordpress/scripts does all that FOR you?

And with no WebPack config (unless you want it, and perhaps in a future post, I can show you how to extend the built-in @wordpress/scripts WebPack).

Let me tell you how.

๐Ÿ“ฆ Step 1: Install @wordpress/scripts

In your plugin or theme directory:

npm init -y
npm install @wordpress/scripts --save-dev

๐Ÿ› ๏ธ Step 2: Configure package.json

Add custom scripts:

"scripts": {
  "build": "wp-scripts build",
  "watch": "wp-scripts start",
  "watch:hot": "wp-scripts start --hot"
}

Optionally, you can specify specific JavaScript files to compile, or even a custom output path:

"scripts": {
  "build": "wp-scripts build assets/src/one.js assets/src/two.js --output-path=assets/dist/",
  "watch": "wp-scripts start assets/src/one.js assets/src/two.js --output-path=assets/dist/",
  "watch:hot": "wp-scripts start assets/src/one.js assets/src/two.js --output-path=assets/dist/ --hot"
}

๐Ÿ“ Step 3: Set Up Your Project Structure

assets/
  src/
    index.js     (or index.ts)
    style.scss
  build/
    index.js
    index.asset.php
    style.css

Inside index.js, import styles directly — this will make it so we don’t have to tell the wp-scripts handler to compile them individually:

import './style.scss';

โš™๏ธ Step 4: Compile Assets

Build assets for production:

npm run build

For development (watch mode):

npm run watch

For hot module replacement (HMR) mode:

npm run watch:hot

This will output the following files:

build/index.js

build/index.asset.php

build/style.css

๐Ÿงฉ Step 5: Register and Enqueue in PHP

Notice the build/index.asset.php? The reason that’s there is so we can auto-register it so we can enqueue it wherever we need it.

Here’s a generalized function that will handle auto-registration for you (NOTE: you should customize the my-plugin parts to fit your plugin/theme so it doesn’t get confused with other plugins or themes):

function register_assets() {
	$asset_root = plugin_dir_path( __FILE__ ) . 'assets/build/';
	$asset_uri  = plugin_dir_url( __FILE__ ) . 'assets/build/';
	$asset_files = glob( $asset_root . '*.asset.php' );

	// Load runtime if present (used by webpack for chunking).
	if ( true === is_readable( $asset_root . 'runtime.js' ) ) {
		enqueue_script(
			'my-plugin/runtime',
			$asset_uri . 'runtime.js',
			array(),
			filemtime( $asset_root . 'runtime.js' )
		);
	}

	foreach ( $asset_files as $file ) {
		$script_meta = require $file;
		$slug = basename( $file, '.asset.php' );

		$handle = "my-plugin/{$slug}";
		$js_path = $asset_root . "{$slug}.js";
		$js_uri  = $asset_uri . "{$slug}.js";
		$css_path = $asset_root . "{$slug}.css";
		$css_uri  = $asset_uri . "{$slug}.css";

		if ( true === is_readable( $css_path ) ) {
			wp_register_style(
				$handle,
				$css_uri,
				array_filter( $script_meta['dependencies'], 'wp_style_is' ),
				$script_meta['version']
			);
		}

		if ( true === is_readable( $js_path ) ) {
			wp_register_script(
				$handle,
				$js_uri,
				array_filter( $script_meta['dependencies'], 'wp_script_is' ),
				$script_meta['version'],
				array( 'in_footer' => true )
			);
		}
	}
}

add_action( 'init', 'register_assets' );

And now you can easily enqueue the scripts where you need them, like this:

function enqueue_my_scripts() {
    wp_enqueue_script( 'my-plugin/index' ); // NOTE: 'index' comes from index.asset.php.
    wp_enqueue_style( 'my-plugin/index' );
}

add_action( 'wp_enqueue_scripts', 'enqueue_my_scripts' );

๐Ÿงผ Bonus: Keep It Clean

Add the following to .gitignore so it does not get included in your source code (unless you want it to be!).

assets/build/

You can build before deployment or commit the built assets as needed.