How to Develop a WordPress Plugin Using Webpack 3 & React (Part 1)

If you’ve been working with JavaScript in the last couple years you’ve probably heard of Webpack. It’s pretty essential in today’s JavaScript workflow and has taken the spot of other build tools like Grunt and Gulp. The WordPress core team is planning to switch to Webpack, so I thought it was high-time to see how Webpack could be integrated into a plugin development workflow.

In this article, we’ll go over how to build a React plugin interface using Webpack and all (well, most of) the bells and whistles. I’ve released the starter plugin on Github so you can follow along with a live example.

What is Webpack Anyway?

Ok so what the heck is Webpack anyway? In short, Webpack is an asset bundler, which means it bundles your JavaScript, CSS, images and other assets together into one file.

Wait, what?

Why would you want this? Well the purpose of Webpack really boils down to the concept of code splitting and application structure. With ES2015, you can import assets and dependencies quite easily and with Webpack and the appropriate loader, you can even import CSS/Sass/Less and images with your JavaScript.

For example, with the style and css loaders you can do things like this:

//App.css
body{
    background: blue;
}

//App.js
import React from 'react';
import './App.css';

export default class App extends React.Component {
    render() {
        return (
            <div>
                <h1>Hello World</h1>
            </div>
        );
    }
}   

The import './App.css'; line embeds the contents of App.css in our markup so that the styles can be applied to the page.

Webpack is quite a large topic, so we’ll go over some of the main concepts and gotchas when it comes specifically to working with WordPress.

Configuration

The first thing to know about Webpack is that by default you can get things going without any config, simply using the CLI interface.

./node_modules/.bin/webpack src/index.js dist/bundle.js

The above command will take the src/index.js file with it’s dependencies and compile it to dist/bundle.js.

However, in most cases you’ll use a config file with Webpack, so that’s what we’ll be doing as well. The default configuration file is webpack.config.js, but you can specify other files with the --config flag if need be. The cool thing about Webpack is that it lets you define dependencies in JavaScript. You can use ES2015’s import function to load in whichever file you need.

In the config file you need to define the entry point(s) and an output location. The output file can be named anything but it’s typically bundle.js. An example webpack.config.js file could look like this.

const path = require( 'path' );
const webpackConfig = {
    entry: [
        './src/index.js'
    ],
    output: {
        filename: 'bundle.js',
        path: path.resolve( __dirname, 'dist' )
    }
};

module.exports = webpackConfig;

Here the entry file is located at ./src/index.js and the output file ./dist/bundle.js.

Loaders

The import './App.css' line in our earlier example includes our styles in our JavaScript. Cool right? You can do the same with images, and arbitrary text files as well, as long as you include the correct loader. Loaders are described as ‘pre-processors’ that can transform files from other languages or formats into JavaScript.

Let’s look at how loaders work. For the styles example we’ll have to get a couple of NPM packages installed.

yarn add style-loader css-loader --dev

We’re using Yarn here, but you could also use plain ‘ol npm. Once these packages are installed we’ll need to update our webpack.config.js file adding these new loaders.

  module: {
    rules: [
      {
        test: /\.css$/,
        use: [
          { loader: 'style-loader' },
          {
            loader: 'css-loader',
            options: {
              modules: true
            }
          }
        ]
      }
    ]
  }

Without getting too far into the weeds here, the module.rules functionality allows us to specify loaders and options based on the test regex. In this case we’re just handling anything that ends in .css.

And that’s really it, you can now use import './App.css' to include CSS in your JavaScript app. Cool beans!

Webpack Plugins

The next item to cover is plugins. Webpack Plugins are basically loaders with more muscle. They let you include other functionality that is more complex and generally work at the end of the compilation phase. They usually work with the compiled bundle, rather than with individual files like loaders do. Loaders also typically work before files are loaded in. So you can think of it as loaders = before compilation; plugins = after compilation + 💪.

WordPress and BrowserSync

In this example we’re using the BrowserSync Webpack plugin. BrowserSync will be our ‘live reload’ tool, reloading the page whenever our files change.

Webpack, however, does have a recommended dev server called (unsurprisingly) webpack-dev-server. While it’s great for static sites, it’s fairly difficult to get configured and working with another server. In our case, WordPress, and particularly, wp-admin, are PHP apps running under a separate web server (Apache or Nginx). What we need to do is proxy our WordPress site and inject our Webpack bundle. I gave webpack-dev-server a fair shot, but ran into too many issues. BrowserSync just works® so that’s what we’re going with!

To get BrowserSync running with Webpack we just need to install a couple packages.

yarn add browser-sync browser-sync-webpack-plugin --dev

In our webpack.config.js the below is all we need to add to get BrowserSync working.

plugins: [
    new BrowserSyncPlugin( {
            proxy: config.proxyURL,
            files: [
                '**/*.php'
            ],
            reloadDelay: 0
        }
    ),
]

config.proxyURL is the URL to our WordPress plugin admin page, in my case http://plugins.dev/wp-admin/tools.php?page=wp-react-boilerplate but this URL can be set in config.json. We’re only watching PHP files with BrowserSync because Webpack is handling the JS and CSS files for us.

I should mention that by not using webpack-dev-server we’re kind of going against the grain in terms of React development. The great thing about webpack-dev-server is that it gives you Hot Module Replacement (HMR). The selling point of HMR is that it doesn’t require a whole page refresh. Only the components that have changed are refreshed, as if by magic.

In our case, a full page reload is ok for now, and there are ways to get HMR going with BrowserSync. I just couldn’t get any existing plugin running with Webpack 3 😩.

Building for Production

The last item to cover in terms of Webpack configuration is building for production. React can be quite hefty when uncompressed, so we’ll need to make sure we minify React and our own code so that it loads as quick as possible. Luckily, Webpack makes this really easy for us. We’ll add a script to run the build to save us some typing.

yarn build is an alias for NODE_ENV=production ./node_modules/.bin/webpack. What this command does is set the NODE_ENV environment variable to production, telling any modules to do ‘production’ stuff – like minify and/or optimize imports.

In our webpack.config.js file we’ll add a little bit more configuration to set some things up for just when we run a build.

if ( process.env.NODE_ENV === 'production' ) {
    const buildFolder = path.resolve( __dirname, 'wp-react-boilerplate-built' );
    webpackConfig.plugins.push( new webpack.optimize.UglifyJsPlugin( {
        "mangle": {
            "screw_ie8": true
        },
        "compress": {
            "screw_ie8": true,
            "warnings": false
        },
        "sourceMap": false
    } ) );

    webpackConfig.plugins.push(
        new CleanWebpackPlugin( [ buildFolder ] )
    );

    webpackConfig.plugins.push(
        new CopyWebpackPlugin( [
            { from: path.resolve( __dirname, 'server' ) + '/**', to: buildFolder },
            { from: path.resolve( __dirname, 'wp-react-boilerplate.php' ), to: buildFolder },
        ], {

            // By default, we only copy modified files during
            // a watch or webpack-dev-server build. Setting this
            // to `true` copies all files.
            copyUnmodified: true
        } )
    );

    webpackConfig.output.path = buildFolder + '/dist';
}

What that whole swath of JavaScript does is tell Webpack to only run the following config if we’re in ‘production’ mode. We add a few more plugins to our config, webpack.optimize.UglifyJsPlugin() will minify and uglify our JavaScript to make it more performant. We then use the CleanWebpackPlugin() to remove the build folder so we can start fresh on each new build. And lastly, we use the CopyWebpackPlugin() to copy over our PHP files to finish building our plugin. Notice that we’re only copying one PHP file and the server directory. All of the other files aren’t needed after the build as all our JavaScript and CSS is one file – bundle.js.

The completed Webpack config is all set, so we’re ready to move onto making our plugin admin page.

Integrating with WordPress

So far we’ve covered all the Webpack stuff, but what about turning this into a WordPress plugin? Welp, that’s the easy part. If you check out the wp-react-boilerplate.php file, you’ll see how much logic is required to set up a new admin page under the ‘Tools’ menu item.

public function admin_menu() {
    $title = __( 'WP React Boilerplate', $this->plugin_domain );

    $hook_suffix = add_management_page( $title, $title, 'export', $this->plugin_domain, array(
        $this,
        'load_admin_view',
    ) );

    add_action( 'load-' . $hook_suffix, array( $this, 'load_bundle' ) );
}

We’re using the admin_menu action call in the class constructor, which invokes the above method. All we’re doing is adding a management page to house our React app. Easy-peasy. Then we add an add_action() call to load the dist/bundle.js file with a wp_enqueue_script()

public function load_bundle() {
    wp_enqueue_script( $this->plugin_domain . '-bundle', plugin_dir_url( __FILE__ ) . 'dist/bundle.js', array(), $this->version, 'all' );
}

And that’s pretty much all there is on the WordPress side (for now). I also created a ./server folder to hold other PHP views and logic, but using it is optional.

Next Steps

You’ve finished part one ‎🎉 In the next part, we’ll go over how to build out the React side of the plugin (make sure you’re subscribed below if you want to be sure to see it).

The plugin is ready to go now, but it doesn’t do too much. Next time we’ll look at how to set up the REST API and configure React to talk to these end-points. We’ll also cover things like CSS pre-processors and other fancy things like animations.

Have you managed to integrate modern JavaScript and WordPress? What are your thoughts on Webpack? Let us know in the comments!

About the Author

Peter Tasker

Peter is a PHP and JavaScript developer from Ottawa, Ontario, Canada. In a previous life he worked for marketing and public relations agencies. Loves WordPress, dislikes FTP.

  • Hey, I’m super close to having a plugin starter (boilerplate type thing) that has BrowserSync and Webpack Dev Server (with Hot Module Swap) working. It’s mostly code I’ve tried to adapt from the brilliant guys over at Roots.io and their Sage Theme (https://github.com/roots/sage)

    Would love it if you guys (or anyone else) want to help me get this darn thing working
    https://github.com/mizner/wp-webpack-bs-hmr

  • Micah Miller-Eshleman

    Thanks for sharing! I’m working on an Preact-based WordPress plugin and BrowserSync suggestion was a lifesaver.
    https://github.com/pranksinatra/network-database-search

    • Peter Tasker

      Cool! Yeah my colleague Jeff mentioned trying BrowserSync, works way better than webpack-dev-server for proxying.

  • Danial

    Nice tutorial.

  • Such an amazing tutorial to start react development. Thanks man. Look forward for the next part.

  • Max Maler

    @petertasker:disqus I’m wondering if there is still any useable options framework for WordPress Plugin development? I searched the web and the same ones I always find are: Options Framework, Titan Framework and some uncool stuff… I really love Codestar Framework… It’s the most advanced an beauty looking one I’ve found so far. Sadly all of them are not much maintained… Am I missing something? Is there any other way to build extended options except the build in WP Customizer? But Customizer is a mess!!!
    This comment was marked as spam??? why??? This is a serious question and I hoped to reach out some professionals from here! I just came here from your newsletter! Hope it will not be banned this time 🙁

    • Peter Tasker

      Hi Max,

      If you’re looking for a generic plugin boilerplate, the WordPress Plugin Boilerplate is still my go to. https://github.com/DevinVinson/WordPress-Plugin-Boilerplate.

      There are tons of others, but it’s really dependent on what your plugin is doing. If you’ve got lots of settings you might want to use something else. But yeah there’s a big push to use the Customizer for options these days, and there’s a lot of work in WP Core going into making the Customizer better.

      You could also take a look at what some popular plugins are doing and copy what they’re doing options wise.