In a previous post, we talked about adding webpack to your WordPress theme and covered the basics of installing and including it in your theme’s dependencies. The next step for any webpack theme setup is to import your source files using an entry point file. In some cases (for example with CSS preprocessors or modern JS) you may need to include special instructions for how you want webpack to process the contents of these files. In this post we’ll take the existing JS and SCSS assets included with WordPress’s underscores starter theme, and compile them into minified assets that can be added to your WordPress front-end.

1. Add File Loaders for Styles
The styles for the underscores theme are written using SCSS, or Sassy CSS syntax. These files need to be consumed by a preprocessor to be compiled into valid CSS. webpack lets us do this using a combination of loaders and module rules.
The first thing we’ll want to do is use npm to install the necessary packages and save them to our devDependencies in package.json
:
npm i css-loader sass-loader node-sass mini-css-extract-plugin -D
The css-loader
module handles CSS files for webpack, sass-loader
does the same for SCSS files, and node-sass
is a wrapper that essentially lets webpack compile SCSS files using the LibSass library. The final package is mini-css-extract-plugin
which will let us extract the compiled CSS from the bundled JS file that webpack generates and place it in its own .css
file.
We’ll add these tools to our project using our webpack.config.js
file. First, require the Mini CSS Extract Plugin:
const MiniCssExtractPlugin = require('mini-css-extract-plugin');
We can then add a set of rules for how webpack should handle SCSS files to your module.exports
. The test
option tells webpack to look for files with a .scss
extension and then use sass-loader
to compile SCSS into CSS, css-loader
to handle the results, and then pass them MiniCssExtractPlugin
:
module: {
rules: [
{
test: /\.scss$/,
use: [
MiniCssExtractPlugin.loader,
'css-loader',
'sass-loader'
]
}
]
},
And then next, we’ll configure the Mini CSS Extract Plugin and tell webpack to handle the extracted CSS by saving it in a file named after our entry point and placing it to the ./dist
folder:
plugins: [
new MiniCssExtractPlugin({
filename: '[name].css'
})
]
To view a Git diff of just the changes made in this step, check out this commit.
2. Add File Loaders for Scripts
Good news! You don’t need to do anything for this step because webpack compiles (and minifies!) javascript out of the box. That said, if you’re like most people and writing modern JS (ex: ES6) for your theme, you’ll want to add the babel-loader module to your webpack.config.js
to transpile JS, just like your SASS loader. We’ll omit this step for now since underscores uses jQuery for their JavaScript files. This means we don’t need a transpiler, and in this case it would unnecessarily slow compile times. Maybe in a future post we can cover re-writing these files using modern JS.
3. Optimize and Minify Built Assets
Production mode in webpack 4 minifies javascript by default. Minifying CSS is optional of course, but here’s how to do it.
Just like compiling SASS, we’ll need to install an npm package to include CSS minification to our webpack toolchain:
npm i optimize-css-assets-webpack-plugin -D
For this operation, we’ll want to require the following two components in our webpack.config.js
file.
const UglifyJsPlugin = require('uglifyjs-webpack-plugin');
const OptimizeCssAssetsPlugin = require('optimize-css-assets-webpack-plugin');
Then we can configure how webpack optimizes our files using the optimization settings block inside module.exports
:
optimization: {
minimizer: [
new UglifyJsPlugin({
cache: true,
parallel: true,
sourceMap: true
}),
new OptimizeCssAssetsPlugin({})
]
},
These config options do two things. First, new OptimizeCssAssetsPlugin({})
adds CSS minification to our project using the default options. And second, because we’ve now customized the optimization
settings in our config, the webpack defaults are overridden. So we need to explicitly include a JS minimizer, in this case using webpack’s default UglifyJsPlugin
.
This guarantees that both our JS and CSS output will be minified. For a Git diff of the changes outlined in this step, have a look at this commit.
4. Move Theme Assets to Source Folder
Now webpack is ready to compile our different asset file types. It’s time to do some housekeeping and move our existing CSS and JS into our webpack project directories. In the case of underscores, there is a ./js/
and a ./sass/
folder in the project root. We want to move these files* into our ./src/
, or webpack source, folder.
*Everything with the exception of ./js/customizer.js
, which is enqueued conditionally by WordPress and should not be part of the global site
bundled assets.
How you want to organize your files beyond this is completely up to you. However, there are some common conventions and general best practices to follow. For example, you’ll probably want to give each entry point its own component folder inside ./src/
. For now we’re starting with just ./src/site/
for our global scripts and styles, but you may want to add things like ./src/home
, ./src/single
, or ./src/admin
for other WordPress templates later on. What’s important is that you have a clear path to an entry point file (like ./src/site/index.js
in this case) for each component, and that you don’t add anything manually to your ./dist/
build folder.
For this project, we can do the following:
- Move and rename
./sass/
to./src/site/scss/
- Create a new folder at
./src/site/js/
and move./js/navigation.js
and./js/skip-link-focus-fix.js
into this directory. - In the
./src/site/scss
directory, renamestyle.scss
toindex.scss
Your resulting webpack ./src/
directories should look something like this:
project
├── src
│ └── site
│ ├── js
│ │ └── ..
│ ├── scss
│ │ └── ..
│ └── index.js
└── dist
├── site.css
└── site.js
We could further optimize things by moving some of our variable and mixin style sheets into a directory at ./src/core/scss
, but we can worry about that once we start including additional entry points in the theme.
And last but not least, we’ll want to clean up our ./style.css
file. This file is required by WordPress because it contains theme meta information, but in our case we’ll be building and enqueueing our own stylesheet elsewhere. So in this case, we can remove basically everything except the theme meta information in the header comment section. (Basically delete everything after Line 21.) And likewise, we can also delete the theme meta information from our ./src/site/scss/index.scss
file so its not included in our bundled assets.
For a visualization of the file changes in this step, you can look at this commit, or for specifics on what was deleted from ./style.css
, check here.
5. Import Asset Files Via Site
Entry Point
Now we can let webpack know there are files we want to include in this bundle by importing them in our ./src/site/index.js
file:
// Global Javascript
import './js/navigation';
import './js/skip-link-focus-fix';
// Global Styles
import './scss/index.scss';
Note that JavaScript files don’t require an extension because webpack looks for them by default. For any other file type you’ll want to include one.
6. Run webpack Build
With the file loaders in place, the theme assets added to our source folder, and webpack set up to import the files, we’re ready to use the shortcut command we added to our package.json file in Part 1 to run our first production build of our webpack ready theme:
npm run build
This should update the contents of your ./dist
folder to now include a minified site.js
file which contains the starter theme scripts, and a minified site.css
, which contains the theme’s global stylesheet.
Next Step, Enqueue Bundled webpack Assets With WordPress
Our theme’s global CSS & JS files are now concatenated, minified, and ready to be added to our WordPress front-end. For a Git diff of all the changes outlined here, take a look at this pull request.
Check back next week for Part 3 of adding webpack to WordPress: Importing and enqueueing your compiled theme assets.