How to Configure Webpack with WordPress for Theme Development the Right Way

Example repo: Download here

Webpack is an awesome static module bundler, based on node.js. It is widely used for developing JavaScript applications. It is a tool that is not easy to set up but comes preconfigured with many frontend application packages such as create react app or @vue/cli. If you have developed single-page applications based on modern JavaScript frameworks such as React, Angular or Vue, you probably know Webpack as the magical wand that minifies your js and css files behind the scenes. Webpack gives the developer the superpower to use ES6 modules to separate js code into components, preprocess scss into css, concatenate and minify scripts, and even auto-refresh the browser each time the css or js code gets updated. However, not many developers know that you can actually take advantage of Webpack’s great features in a monolithic server atmosphere, such as WordPress. Just because you are working on a server-side rendered WordPress website, does not mean you should not use modern frontend tools, right? In fact, do feel encouraged to use it, as this would give you an edge in a very competitive niche. In this tutorial, I will show you how to bundle Webpack with WordPress for theme or plugin development using @wordpress/scripts tool.

All the code in this tutorial is publicly available and can be downloaded from the open-source starter WordPress-Webpack theme that I have created. The theme comes prebundled with a basic Webpack configuration, which you can easily extend. Now, enough talking, let’s grab a cup of coffee and start coding.

HostArmada Affordable Cloud SSD Shared Hosting

Configure Webpack

If you want, you can download Webpack and set it up from scratch, but we are going to use something else here, which is specifically tailored for WordPress development, called @wordpress/scripts. It is a collection of reusable scripts that the WordPress core team uses to develop and maintain the WordPress Block Editor. As you probably know, the WordPress Block editor (Gutenberg Editor) is built primarily on React.js, so these scripts are an absolute must. So, instead of configuring Webpack manually for theme development, which would take too much time, even if you are an experienced developer, I would highly recommend you run the following command in cmd or terminal:

npm install @wordpress/scripts --save-dev

P.S: If this does not work, make sure you have installed node and npm on your machine. You would be using node modules, so you need node.js, come on!

At this point, your package.json file should look like something like this:

package.json

{
    "name": "highstarter_child",
    "version": "1.0.0",
    "description": "A child theme of the Highstarter theme",
    "scripts": {
        "build": "wp-scripts build",
        "start": "wp-scripts start",
        "lint:css": "wp-scripts lint-style",
        "lint:js": "wp-scripts lint-js",
        "format:css": "npm run lint:css -- --fix",
        "format:js": "npm run lint:js -- --fix"
    },
    "devDependencies": {
        "@wordpress/scripts": "^24.6.0",
    },
    "author": "Atanas Yonkov",
    "license": "GNU GPL"
}

Next, you can optionally create a webpack.config.js file, where you can extend the default Webpack configuration of @wordpress/scripts and configure where the source files will reside.

webpack.config.js

/**
 * External Dependencies
 */
const path = require( 'path' );

/**
 * WordPress Dependencies
 */
const defaultConfig = require( '@wordpress/scripts/config/webpack.config.js' );

module.exports = {
	...defaultConfig,
	...{
		entry: {
			main: path.resolve( process.cwd(), 'src/scss', 'main.scss' ),
			app: path.resolve( process.cwd(), 'src/js', 'app.js' ),
		},
	},
	plugins: [ ...defaultConfig.plugins ],
};

With this configuration, I have created a src folder and added scss and js folders inside it. Finally, I added main.scss file inside the scss folder and app.js file inside the js folder. You can change that accordingly, but keep in mind that with the current configuration, Webpack will output the dist files inside build folder.

Set Up ES Lint

@Wordpress/scripts also includes a linter that will keep your js and scss files tidy and clean. It is highly recommended to add a .eslintrc file in the root of your theme. Here is the configuration I am using the following:

.eslintrc

{
	"extends": [
		"plugin:@wordpress/eslint-plugin/recommended-with-formatting"
	],
	"env": {
		"browser": true,
		"es6": true
	},
	"rules": {
		"@wordpress/no-global-event-listener": "off",
		"eqeqeq": "off"
	}
}

You can add your own set of rules, the important thing here is to be consistent. To check the code inside the src folder for errors, run:

npm run format:js

Generate the build folder

By default, Webpack should generate the output from the src folder into the build folder. Add some test js code in src/js/app.js and run:

npm run start

This should allow Webpack to generate the build folder with app.js main.css and app.asset.php. To minify the files for production, run

npm run build

From the files that Webpack generates inside the build folder, the most interesting one is app.asset.php. It produces a list of all externally imported modules you have used in your src/js/app.js file. You can then use this file to automatically add them as dependencies to your script. For example, if you install jquery as npm package and import it into your app.js file, the app.asset.php file will automatically get updated by Webpack and WordPress will know to add it as a js dependency. For this, you would need to enqueue your build/app.js file too. More on that in the next chapter.

HostArmada Affordable Cloud SSD Shared Hosting

You may notice that with the current configuration, Webpack generates one additional .js file, called main.js. This is because, by default, Webpack generates a .js file for every entry, even if it is actually a css file. To avoid this, I have added two additional Webpack plugins as npm dev dependencies:

npm install --save-dev mini-css-extract-plugin
npm install --save-dev webpack-fix-style-only-entries

Enqueue your assets

Last, but not least, you need to include the assets that Webpack generates inside the build folder. If you have kept the same structure like me, add the following code to functions.php:

if ( ! defined( 'HIGHSTARTER_CHILD_VERSION' ) ) {
	// Replace the version number of the theme on each release.
	define( 'HIGHSTARTER_CHILD_VERSION', '1.0.0' );
}

function highstarter_child_webpack_scripts_and_styles() {
	$script_asset = include_once( get_stylesheet_directory() . '/build/app.asset.php' );
	wp_enqueue_script( 'custom-js', get_stylesheet_directory_uri()  . '/build/app.js', $script_asset['dependencies'], $script_asset['version'], true );
	wp_enqueue_style( 'custom-css', get_stylesheet_directory_uri()  . '/build/main.css', array(), HIGHSTARTER_CHILD_VERSION );
}
add_action( 'wp_enqueue_scripts', 'highstarter_child_webpack_scripts_and_styles', 100 );

The beautiful thing here regarding the .js file is that it will automatically update its js dependencies based on the modules that you have imported in app.js file inside the src/js folder. It will also update its version automatically, based on the time Webpack has updated the build folder.

Import Modules in Your Js File

Inside src/js, create a components folder and add a file called theme-options.js:

theme-options.js

export default function themeOptions() {
	// eslint-disable-next-line no-console
	console.log( 'A foo walks into a bar, takes a look around and says "Hello World!"' );
}

We have just created a module. Now, let’s import it in our app.js file:

app.js

/**
 * Internal Dependencies
 */
import themeOptions from './components/theme-options';

( function() {
	themeOptions();
}() );

Now, if we run npm run start and check the build folder, we will see that the js has successfully been compiled. In the same way, we can use the import statement inside src/js/app.js to use external dependencies, e.g. jQuery. Webpack will take care to concatenate and minify everything into one small file, enabling us to experience a modern and sophisticated frontend development experience.