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.cssInside 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 buildFor development (watch mode):
npm run watchFor hot module replacement (HMR) mode:
npm run watch:hotThis 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.