Incorporting Webpack into a WordPress theme (part 1)
Webpack has been around for a few years now and has clear benefits for bundling and serving front-end code. It is not a task manger like Grunt or Gulp but rather a “module bundler for modern JavaScript applications”. Although it might not sound appropriate for a WordPress theme at first glance it can be well suited and provides numerous opportunities to modularise front-end code which can map well to theWordPress template structure.
Part 1- Basic Setup
The end goal will be to have front-end code that specifically relates to the page-template in question. For example, on the contact page we don’t need all the same css and js that we have on the home page. The CSS and JS concerning comments would only be required where the comments are actually present and so-forth.
Webpack allows for something called “chunking” which means that it will include only the appropriate scripts and styles needed for a designated “chunk”. When building a WordPress theme we are already doing something similar. We create specific page templates that enable us to limit the code to that required page. Whenever we add a specific widget or piece of functionality we can extend that code. Very often the styling and client-side interactions are on a per page basis so that a widget may be styled differently in different contexts or the same no matter what the context. Webpack allows us to specify how this is done.
As of this writing Webpack is at v3.5.6 and the documentation is quite good. The site includes a small button on the right side of the page that allow you to ask questions in chat form. It is important to defer to the documentation for the version of Webpack you are using as there are often subtle differences between versions.
You will need node.js coupled with node package manager (npm) installed globally on your machine. If you do not already have it you can get it by following this link.
Here we will build a custom theme where functionality is kept to a minimum. These same techniques can be applied to a kitchen sink type theme that does everything from blogging to making toast but here we are going to assume a very simple blogging theme. I am going to assume that anyone reading this article has basic knowledge of using and writing code for WordPress theme.
Although you can easily convert any theme to use WebPack, it is probably easier to start with an empty starter theme. Personally, I rather like _s but any starter-type theme will do.
We will be installing Webpack and other packages using npm, a popular frontend package manager. npm init
will offer several prompts which you can either fill out or not. Keep in mind that any file with a .js suffix in your root folder will be potentially offered as a default. It is customary to use index.js or app.js as an entry point. We are also going to create 2 folders src
which will contain our raw assets and dist
where our bundled files will reside. We will put out entry point in the src folder to keep our theme root a bit tidier.
This will create a package.json file in your project that you can modify as needed.
In order to run webpack commands from our machine we will need to install Webpack globally. You can install it on a Virtual machine if you are using one to run WordPress but this will mean that you have to ssh into the machine in order to run the webpack commands. You can install it with
sudo npm install -g --save webpack
You will also need to create a webpack.config.js file at the root of your project. Although this is not strictly necessary and you can specify options using the Webpack CLI, I find it to be pretty much indispensable. In this example we will start with a basic entry and output like so:
const path = require('path');
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, 'dist'),
filename: "bundle.js"
}
}
ìndex.js
is the control centre for all our Webpack assets which we will house in the src folder. They will then be output to a file named bundle.js
in the dist
folder. You don’t have to use these specific names for you files but it is important to be consistant. If you are curious about the path_resolve(__dirname, 'dist')
, that comes from node and will return the first absolute path from the current directory. From the theme directory in our case that would be /dist/
.
We’ll come back to this in a bit but right now we are going to see about our module rules. We will start with an object named module to which we will pass options determining how the different modules in our theme will be treated. We will define an array rules that match using regex.
For our first loader we are going to match all files ending in js. Since I like to use ES6 code in my projects I use babel-loader in order to polyfill for browsers that do not support ES6.
First we will need to install babel loader a a few other dependancies
npm install --save-dev babel-loader babel-core babel-preset-env webpack
Then inside our module.exports we will include:
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
}
]
}
An object can be added for each of the rules. Each rule needs a test property and a loader. You can find the correct configuration of the object in the README of the loader. For example, we will need a loader for our css and according to the Webpack guide we can use style-loader. In this case I can copy the test object from the guide page but I might want to add further options. In either case we will need to get the style loader.
npm install style-loader file-loader --save-dev
I can Google style-loader and find this page. Here I have a more advanced options for the style loader. The default behaviour of style-loader is to insert your css between style tags in the head of the page. We are going to modify this but in the interest of having something that works we will expand our rules array like so :
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
},
{
test: /.css$/,
use: [
'style-loader',
'css-loader'
]
}
]
}
The next test case will take care of our css. Eventually we will modify this to output actual stylesheets but we are going to leave that job to a Webpack plugin. For now we will be able to see the css styles that we include in our theme.
Our next task will be to include images in our theme. These will be primarily stylistic images like background images as our content images will be served from WordPress itself. Nothing complicated here, we already have a file-loader installed so we’ll just add a test case for our various image types.
{
test: /.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
Now we can add the appropriate files to our theme. In the src directory we will create three folders css, js, and img. In these folders we will add style.css, scripts.js, and an image of your choosing which you can call image.png (or whatever you like)
In order to make any of our assets available we are going to have to add an imports statement to our index.js. For now we have three:
import './css/main.css';
import './js/scripts.js';
import './img/bg.gif';
There is one more adjustment to make to get our CSS working. As it stands the styles are set up to be injected into the page which is not really what we’re going for. Our end goal is to have Webpack to build stylesheets that are page-specific. For that we need to our styles to be extracted. Luckily Webpack has a plugin that allows for this. It is called ExtractTextPlugin and will allow us to create css files that we can add styles to the head of the pages in question. First we will install the plugin
npm install --save-dev extract-text-webpack-plugin css-loader
Next we need to require it at the top of our webpack.config file.
const ExtractTextPlugin = require("extract-text-webpack-plugin");
Now we will modify our css rule
{
test: /.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
importLoaders: 1,
sourceMap: true
}
}
]
})
}
We also need to add a new property to our module
plugins: [
new ExtractTextPlugin("style.css")
]
Altogether we now have
const path = require('path'); const ExtractTextPlugin = require("extract-text-webpack-plugin");
module.exports = {
entry: "./src/index.js",
output: {
path: path.resolve(__dirname, "dist"),
filename: "bundle.js"
},
module: {
rules: [
{
test: /.js$/,
exclude: /(node_modules)/,
use: {
loader: 'babel-loader',
options: {
presets: ['env']
}
}
},
{
test: /.css$/,
use: ExtractTextPlugin.extract({
fallback: 'style-loader',
use: [
{
loader: 'css-loader',
options: {
importLoaders: 1,
sourceMap: true
}
}
]
})
},
{
test: /.(png|svg|jpg|gif)$/,
use: [
'file-loader'
]
}
],
},
plugins: [
new ExtractTextPlugin("style.css")
]
}
Now all that’s left is to enqueue our new stylesheet into the head of our theme
function mytheme_styles() {
wp_enqueue_style( 'mytheme-common', get_template_directory_uri() . '/dist/main.css', array(), date("H:i:s"));
}
add_action( 'wp_enqueue_scripts', 'mytheme_styles' );
With all this in place you can run webpack
from the command line in your theme directory. This will start the magic and churn out a dist folder filled with you assets. Upon viewing your theme in the browser you should be able to see everything in some sort of working order. In your dist folder you should have a file called bundle.js and a hashed filename for your image. Neither of these files is going to do us much good for the moment. There is too much code in our bundle.js and the hashed image file doesn’t suit us very well for theme building.
We have completed the first phase of incorporating Webpack into a Wordpress theme. Our next step will be to fine-tune all of this so that we can take advantage of code-splitting and eventually tree-shaking. We will also add some npm scripts so that developing our theme is a bit easier.
You can read on in Part 2 here.