React WP theme: structure, Node packages and Webpack

In the second part of our tutorial, we’ll be talking about the theme’s initial folder/file structure, installing required Node packages, configuring Webpack and setting up a Node development server with hot reloading. I’ll show you how to properly bootstrap a Redux-powered WordPress theme. If you want to see how the project will look at the end of this article, look at its GitHub repository (branch 1_structure_node_webpack).

Tutorial navigation

  1. React single-page WordPress REST API theme tutorial
  2. React WP theme: structure, Node packages and Webpack (current)
  3. React WP theme: Smart vs Dumb components + React Router
  4. React WP theme: Creating dumb components
  5. .
    . to be written
    .

Theme folder structure

Create a basic WordPress theme: index.php, style.css, header.php, footer.php. We don’t need anything special, just to have a valid theme.

Now, create these files in the root of our theme:

  • `package.json` — manage npm packages (Node) (similar to composer.json)
  • `webpack.config.js` — main configuration file for Webpack
  • `server.js` — configuration file for our Node dev server

We also need to create an src/ folder where the client-side app will live. We won’t writing much custom CSS due to using the excellent Bootstrap framework, but we should organize the Bootstrap’s CSS a little so create a folder css/ where we’ll put it.

 

Figure1: Final structure of our theme
Figure1: Final structure of our theme

 

Installing Node packages

Before we can start writing Webpack configuration, we need to install some Node packages. Keep in mind that React, Redux, and all the other libraries are always in development so the version we’ll be using in this tutorial will likely change while we complete it.

Our main dependencies are React and Redux. Although Redux plays nicely with React, they are not connected by default. That’s why we also need a react-redux library which has the bindings from React to Redux. The next thing we need is React-Router, a pretty cool and smallish library for routing in React. In order to have an HTML5-based page transitions (history.pushState), we also need a library called history, which is then used as the “history driver” in React-Router. Aaand, that’s it!

 

Well, I lied. What about development dependencies? Yeah, we need them too. The most important one is Webpack for building and outputting the complete app. Without loaders and plugins, Webpack is (almost) of no use — we need to install them:

  • `babel-loader`: For processing React JSX and ES6. Install also `babel-core`.
  • `css-loader`: It allows us to load CSS like other libraries e.g. `import ../css/bootstrap.css;`.
  • `extract-text-webpack-plugin`: Instead of having the CSS in JavaScript, Webpack outputs it to a separate file.
  • `file-loader`: For loading static files such as images.
  • `style-loader`: What’s the difference between this and `css-loader`? I honestly don’t know.
  • `html-webpack-plugin`: It generates the main HTML file with our app and CSS, useful for development purposes.
  • `react-hot-loader`: One of the coolest things ever. We no longer have to refresh the page when changing a React component (or a Redux reducer) — it will automagically refresh for us on saving the modified file.
  • `webpack-dev-server`: A tiny server for development purposes.

Sigh, that’s a lot of new things to grasp. I say: Have no worries for now. You’ll see how to work with them along the way. What is more, you don’t really need to understand how each one of them works — just be able to use it.

One last thing is to add a few scripts into our packages.json file as shortcuts to start the development server, clean the dist/ folder (where our app is built into) and build the application through Webpack. I’m including the entire file here. You can safely ignore things I didn’t mention.


{
"name": "Lexi-WP-Theme",
"version": "0.0.1",
"description": "",
"license": "MIT",
"dependencies": {
"history": "^1.9.0",
"isomorphic-fetch": "^2.1.1",
"react": "^0.14.0-rc1",
"react-redux": "^3.0.1",
"react-router": "1.0.0-rc1",
"redux": "^3.0.2",
"redux-thunk": "^1.0.0"
},
"devDependencies": {
"babel-core": "^5.6.18",
"babel-loader": "^5.1.4",
"css-loader": "^0.18.0",
"extract-text-webpack-plugin": "^0.8.2",
"file-loader": "^0.8.4",
"html-webpack-plugin": "^1.6.1",
"react-hot-loader": "^1.3.0",
"style-loader": "^0.12.3",
"webpack": "^1.9.11",
"webpack-dev-server": "^1.9.0"
},
"scripts": {
"clean": "rm -rf dist",
"build": "npm run clean && webpack",
"start": "npm run build && node server.js"
}
}

view raw

packages.json

hosted with ❤ by GitHub

Copy the file into your theme’s directory and run npm install from within the command line. All the packages should install in a while. Alright, let’s move to more interesting topic — Webpack!

Configuring Webpack

Don’t be fooled by Webpack’s innocently-looking homepage — there’s a beast underneath it, seriously. Even Facebook (Instagram) is using it, and they’ve written a pretty decent guide on how to work with it. Give it a (brief) read, then continue here. Done? Cool, now you know why Webpack rocks.

When I saw a random wepback.config.js Webpack configuration file for the first time, it looked like Chinese to me (for them like Russian, you know what I mean). I simply began to rewrite (not copy-paste) parts of it into my one, then running npm start and watching it crash. Eventually, I got it working and here are my results.


var path = require('path');
var webpack = require('webpack');
var HtmlWebpackPlugin = require('html-webpack-plugin');
var ExtractTextPlugin = require('extract-text-webpack-plugin');
module.exports = {
devtool: 'source-map',
entry: [
'webpack-dev-server/client?http://localhost:3000',
'webpack/hot/only-dev-server',
'./src/index'
],
output: {
path: path.join(__dirname, 'dist'),
filename: 'bundle.js',
publicPath: '/'
},
plugins: [
new webpack.HotModuleReplacementPlugin(),
new HtmlWebpackPlugin({
filename: 'index.html',
template: './src/index.template.html',
inject: true
}),
new webpack.NoErrorsPlugin(),
new ExtractTextPlugin("style.css", {
allChunks: true
})
],
module: {
loaders: [
{
test: /\.js$/,
loaders: ['react-hot', 'babel'],
exclude: /node_modules/,
include: __dirname
}, {
test: /\.css$/,
loader: ExtractTextPlugin.extract("style-loader", "css-loader")
}, {
test: /\.png$/,
loader: "url-loader?limit=100000"
}, {
test: /\.jpg$/,
loader: "file-loader"
}, {
test: /\.(ttf|eot|svg|woff(2)?)(\?[a-z0-9]+)?$/,
loader: 'file-loader'
}
]
}
};

The first thing you’ll notice is that it’s just a pure JavaScript, which is very nice. At the beginning of the file, we require() a few libraries. Then, we just export a JS object — the Webpack’s configuration:

  • `devtool: ‘source-maps’` means that Webpack will generate source maps for our JS & CSS files.
  • `entry` specifies the main (entry point) file of our web application. It will be the `./src/index.js` file. We’ll create it in the next tutorial. The other two lines (`webpack-dev-server`) configure the development server. Just change the port if you need to (default is 3000)
  • `output` configures the path where Webpack should put the compiled files. We set it to the `dist/` folder and tell Webpack to put everything into the `bundle.js` file. `publicPath` just says that we are on the root path.
  • `plugins` is a list of the Webpack plugins we use in our app. `HotModuleReplacementPlugin` is for the hot reloading of React/Redux code. `HtmlWebpackPlugin` generates an `index.html` file in the `dist/` folder from our HTML template in the `src/` folder (`./src/index.template.html`). The `inject: true` injects all the assets (both css and js) into the head of the index.html file.
  • The `NoErrorsPlugin` has something to do with errors so that no assets with errors will output. Read more.
  • Finally, the `ExtractTextPlugin` moves every imported CSS file into a single `style.css` (`allChunks: true`).

Now the loaders. A loader is a little Webpack “plugin” which knows how to process the imported assets. It’s something like a “task” in other build tools (e.g. Grunt). For us, it is enough to learn how to work with them. Let’s take the first loader and inspect it closer:

{
 test: /.js$/,
 loaders: ['react-hot', 'babel'],
 exclude: /node_modules/,
 include: __dirname
}

We can see that a using a loader is just writing a JS object with a few keys:

  • `test: /.js$/` is a simple regular expression used to select files which the loader should process. In this case, this loader is applied only to files ending in the `.js` file extension.
  • `loaders: [react-hot, babel]`: Here we specify which loaders should be called on the matched files (in our case, all imported JS files). `react-hot` is used for hot reloading of our React components (discussed earlier in this article). Babel is a powerful JS compiler, enabling us to write ES6 JavaScript which gets compiled down to ES5 (supported by all modern browsers), as well as to use JSX for writing React components (which, too, gets compiled to standards JS objects).
  • The `exclude: /node_modules/` instructs Webpack to ignore the `node_modules/` folder as it contains the installed libraries (we did this in the Node chapter above).
  • Finally, the `include: __dirname` just says that Webpack should look for .js files also in the theme’s root folder (`__dirname` is the directory in which the currently executing script resides).

Other loaders follow the same structure, except they deal with different parts of our web app. Study them a bit and move on.

Setting up development server

The last thing we’ll gonna do in this tutorial is to set up a development server. You should already have a WordPress (PHP) server running (use EasyEngine or Ansible for that). The dev server we’ll set up here is not a PHP one, it’s a Node.js one. We need it for the hot reloading to work. It’s also easier and more pleasurable to code our web app this way and then, when we move it to the production server, use only the PHP one.

We configure the whole dev server in the server.js file. Have a look at it:


var webpack = require('webpack');
var WebpackDevServer = require('webpack-dev-server');
var config = require('./webpack.config');
new WebpackDevServer(webpack(config), {
publicPath: config.output.publicPath,
hot: true,
historyApiFallback: true,
stats: {
colors: true
}
}).listen(3000, 'localhost', function(err) {
if (err) {
console.log(err);
}
console.log('Listening at localhost:3000');
});

view raw

server.js

hosted with ❤ by GitHub

In the first few lines, we import the Webpack and the Webpack-dev-server libraries. On the third line, we also import the Webpack’s configuration file that we just created earlier. We’ll need it for a few constants.

Next, we instantiate a new object WebpackDevServer, passing to it the webpack object and a configuration object:

  • `publicPath` is set to the same value as the `publicPath` in our Webpack config file.
  • `hot: true` enables the hot reloading.
  • `historyApiFallback` probably means that if a user’s browser doesn’t support the HTML5 History API, it will use the “hashtag” API. But I don’t know.

Then we configure it to listen on localhost:3000 and if there’s an error, print it to the browser’s console.

Conclusion

Well, that’s it for today. We’ve learned which Node packages we’ll need for our web application to work. We also learned how to use and configure Webpack. Finally, we’ve seen how to set up a simple but useful dev server. I think it’s a lot of information for a single session, so feel free to reread the article and study the links I provided in it. Most importantly, don’t be hard on yourself when you don’t understand it all. It took me some weeks before I could write about it.

In the following article, we’ll start building the actual web app, starting with some React, React-Router, and Redux. Stay tuned!

Also, please, I beg you, ask in the comments below. I know that I didn’t go much deeply in some topics and the comments are the best way to explore them. Let’s have a nice conversation, shall we.

Join the Conversation

8 Comments

  1. Well, this is all still way over my head, but then again, so was Ansible earlier in the year. Keep up the series, hopefully I won’t lose steam on my explorations in the meantime! Thanks!

    Like

    1. Exactly, it’s over my head too. Except for React, I started to explore these waters just a few months ago, so I think you can learn it as well. Just don’t give up!

      I think the reason why this is so complicated is that no big company has created a singular framework/workflow for building sites this way yet. It’s all just developers working on this mostly in their free time. Still too early.

      On the contrary, there is the famous Angular framework from Google which has everything included. But I don’t like it that much, React is ++ 🙂

      Like

  2. Did I get it right that we need NodeJS only for development and can run the final WP+Theme on php server?

    Like

    1. Great question. Well, yes, when the theme is finished, you have a single bundle.js JS file which contains the whole app and is loaded into the WP theme (with like wp_enqueue_scripts), so you don’t need Node anymore.

      Like

  3. HI,I got a question,when I want to get the data from the backend,should I set a proxy in my webpackDevServer?
    I set the proxy like this:
    “`
    proxy:{
    ‘/api/*’: {
    target: {
    “host”: “test.local”,
    “protocol”: ‘http:’,
    “port”: 80,
    “path”:”/wp-json/wp/v2/”
    },
    ignorePath: true,
    changeOrigin: true,
    secure: false
    }
    }
    “`
    no matter i visit the `/api/wp-json/wp/v2/posts` or `/api/wp-json/wp/v2`,it will return the same thing.where is the mistake?

    Like

Leave a comment

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out /  Change )

Facebook photo

You are commenting using your Facebook account. Log Out /  Change )

Connecting to %s

%d bloggers like this: