Using Webpack 5 and SASS with WordPress

Published

A few years go I wrote an article on how to use Webpack to build JavaScript and CSS for a WordPress theme. Some time has passed, and I created an updated guide here. Hope its helpful!

Example Theme Published on GitHub

An example theme containing all of the code discussed in this post is available on GitHub, and includes a barebones WordPress theme with webpack 5, a SASS build, Babel JavaScript transpilation, and sample loaders for webfonts and images. The theme should be ready to download and drop into a running WordPress instance, simply follow the build instructions in the README.

Screenshot of the example theme discussed in this post

Code Structure

How you organize you code is a matter of preference, and I don’t think there is really any wrong way to do it as long as things feel clean and organized. That being said, the code referenced in this post and the sample theme above follows this structure:

# some items removed for brevity
├── js
│   ├── build
│   └── src
│       ├── components
│       │   └── DisplayLabel.js
│       └── main.js
├── css
│   ├── build
│   └── src
│       ├── components
│       │   └── _content.scss
│       └── main.scss
├── img
│   └── background.jpg
├── font
│   └── open-sans
│       ├── opensans-regular-webfont.woff
│       └── opensans-regular-webfont.woff2
├── index.php
├── functions.php
├── package.json
├── package-lock.json
└── webpack.config.js

Webpack Config

Here is the final webpack 5 configuration file that builds JavaScript and CSS for a WordPress template (including SASS and Babel support):

const path = require('path');

// css extraction and minification
const MiniCssExtractPlugin = require("mini-css-extract-plugin");
const CssMinimizerPlugin = require("css-minimizer-webpack-plugin");

// clean out build dir in-between builds
const { CleanWebpackPlugin } = require('clean-webpack-plugin');

module.exports = [
  {
    entry: {
      'main': [
        './js/src/main.js',
        './css/src/main.scss'
      ]
    },
    output: {
      filename: './js/build/[name].min.[fullhash].js',
      path: path.resolve(__dirname)
    },
    module: {
      rules: [
        // js babelization
        {
          test: /\.(js|jsx)$/,
          exclude: /node_modules/,
          loader: 'babel-loader'
        },
        // sass compilation
        {
          test: /\.(sass|scss)$/,
          use: [MiniCssExtractPlugin.loader, 'css-loader', 'sass-loader']
        },
        // loader for webfonts (only required if loading custom fonts)
        {
          test: /\.(woff|woff2|eot|ttf|otf)$/,
          type: 'asset/resource',
          generator: {
            filename: './css/build/font/[name][ext]',
          }
        },
        // loader for images and icons (only required if css references image files)
        {
          test: /\.(png|jpg|gif)$/,
          type: 'asset/resource',
          generator: {
            filename: './css/build/img/[name][ext]',
          }
        },
      ]
    },
    plugins: [
      // clear out build directories on each build
      new CleanWebpackPlugin({
        cleanOnceBeforeBuildPatterns: [
          './js/build/*',
          './css/build/*'
        ]
      }),
      // css extraction into dedicated file
      new MiniCssExtractPlugin({
        filename: './css/build/main.min.[fullhash].css'
      }),
    ],
    optimization: {
      // minification - only performed when mode = production
      minimizer: [
        // js minification - special syntax enabling webpack 5 default terser-webpack-plugin 
        `...`,
        // css minification
        new CssMinimizerPlugin(),
      ]
    },
  }
];

CSS is extracted used the MiniCssExtractPlugin plugin. Hashes are added to the output file names for caching (lines 19 and 63), and the CleanWebpackPlugin plugin is used to clear build artifacts in-between builds. Minimization is included for the JavaScript and CSS (lines 66-74), and is only enabled during production builds (e.g. webpack --mode production).

Incorporating Webpack Assets into the WordPress Theme

The JavaScript and CSS files produced by the webpack build are then loaded into the WordPress theme with an addition to the functions.php:

// register webpack compiled js and css with theme
function enqueue_webpack_scripts() {
  
  $cssFilePath = glob( get_template_directory() . '/css/build/main.min.*.css' );
  $cssFileURI = get_template_directory_uri() . '/css/build/' . basename($cssFilePath[0]);
  wp_enqueue_style( 'main_css', $cssFileURI );
  
  $jsFilePath = glob( get_template_directory() . '/js/build/main.min.*.js' );
  $jsFileURI = get_template_directory_uri() . '/js/build/' . basename($jsFilePath[0]);
  wp_enqueue_script( 'main_js', $jsFileURI , null , null , true );
    
}
add_action( 'wp_enqueue_scripts', 'enqueue_webpack_scripts' );

The glob() function is used to select the JavaScript and CSS files from the build directories, and accommodates the hash added to the filenames (notice the [fullhash] on lines 19 and 63 of the webpack config above).

Once the WordPress theme is reading in the compiled JavaScript and CSS files, the theme is ready to go!

Lessons Learned when Migrating to Webpack 5

The overall approach for incorporating a webpack build into WordPress didn’t change, however there were a few subtleties I discovered when upgrading:

  • Built-in Asset Modules can replace certain webpack loaders like file-loader and url-loader.
  • JavaScript minification is provided out of the box via the TerserPlugin.
  • process.env is no longer polyfilled in webpack 5 and cannot be used in webpack config files without the use of something like dotenv-webpack.
  • My actual blog website webpack build had multiple configurations (i.e. it exported multiple configuration objects). I had to enable parallelism in order for webpack to build more than the first entry..

Subscribe by Email

Enter your email address below to be notified about updates and new posts.


Comments

Loading comments..

No responses yet

Leave a Reply

Your email address will not be published. Required fields are marked *