Dev

By Carlos Santana on
Reading time: 3 minutes

Webpack-ytfX9.png

If you read my last post, Mastering Webpack 4 - Zero Configuration you should understand now how Webpack works in general, now in this post, I'll show you how to implement React with Webpack 4.

The first thing we need to do is to install these packages.

npm install html-webpack-plugin babel-loader react react-dom @babel/core @babel/preset-env @babel/preset-react @babel/register

This plugin helps us to generate the index.html file to render our React application. In the next post, I'll explain how to integrate Node.js to have more flexibility in the configuration of the site and be able to use Server Side Render.

Using the same code of the last post, create a .babelrc file and then add these presets:

{
  "presets": [
    "@babel/preset-env",
    "@babel/preset-react"
  ]
}
File: .babelrc

In the webpack.config.js file, where we have our babel-loader, we need to add the .jsx extension beside the .js extension to be able to apply the babel-loader to our React components:

const webpackConfig = {
  module: {
    rules: [
      {
        test: /.(js|jsx)$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  }
};

module.exports = webpackConfig;
File: webpack.config.js

After we added the .jsx extension to our babel-loader, we have to create an App.jsx file:

// Dependencies
import React from 'react';

// Components
import Home from './Home';

const App = props => <Home />;

export default App;
File: src/components/App.jsx

Now let's create the Home component:

import React from 'react';

const Home = () => <h1>Home</h1>;

export default Home;
File: src/components/Home/index.jsx

Then in our index.jsx file, we have to include React, ReactDOM, and then render our App component: 

// Dependencies
import React from 'react';
import { render } from 'react-dom';

// Components
import App from './components/App';

render(<App />, document.querySelector('#root'));
File: src/index.jsx

You may wonder where the #root div is since we have not created our index.html yet. For this, as I mentioned at the beginning we will use the html-webpack-plugin to process our HTML: 

<!DOCTYPE html>
<html>
  <head>
    <meta charset="UTF-8">
    <title><%= htmlWebpackPlugin.options.title %></title>
  </head>

  <body>
    <div id="root"></div>
  </body>
</html>
File: src/index.html

As you can see, we can inject variables from the plugin using the htmlWebpackPlugin.options object between the <%= and %> delimiters. Now it's time to test the application, try to run the npm run build command:

WebpackError-I7xph.png

We got an error: Can't resolve .src directory..., but what does it mean? Do you remember how we used the .jsx extension in our files? Even we added that extension to the babel-loader rule, so why is not working? It's because we had to add a resolve node to our configuration and specified the file extensions we want to support. Otherwise, we have to use only the .js extension:

const HtmlWebPackPlugin = require('html-webpack-plugin');

const webpackConfig = {
  module: {
    rules: [
      {
        test: /.(js|jsx)$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebPackPlugin({
      title: 'DEV Education',
      template: './src/index.html',
      filename: './index.html'
    })
  ],
  resolve: {
    extensions: ['.js', '.jsx']
  }
};

module.exports = webpackConfig;
File: webpack.config.js

If we run the npm run build again, now it should work: 

WebpackBuildOk-7KW4g.png

After you run the command, you will see that you have two files in your dist directory: index.html and main.js. If you open your index.html file with Chrome, and then you should see the following result: 

DistHome-uiIBZ.png

Great!, now we can build our bundle, but it is 100% static. In the next post, I'm going to explain to you how to implement the Webpack Dev Server to run our React Application in an actual server and refresh the server every time we make a change.

Babel

I always prefer to use ES6 code in all my scripts, even in the configurations, and I like to break my Webpack configuration into separate files for better organization and an easier understanding of the configurations. If you have worked with Webpack before, you know that a webpack.config.js file can be huge and very difficult to maintain, so let me explain to you how to do that:

First, let's rename the webpack.config.js file to webpack.config.babel.js. When we add the .babel suffix on a .js file, this will be handled by Babel automatically, but in Babel 7 (the latest version of Babel) we also need to require the @babel/register at the beginning of the file.

Let's change our current code to be ES6: 

require('@babel/register');

import HtmlWebPackPlugin from 'html-webpack-plugin';

export default {
  module: {
    rules: [
      {
        test: /.(js|jsx)$/,
        exclude: /node_modules/,
        use: 'babel-loader'
      }
    ]
  },
  plugins: [
    new HtmlWebPackPlugin({
      title: 'DEV Education',
      template: './src/index.html',
      filename: './index.html'
    })
  ],
  resolve: {
    extensions: ['.js', '.jsx']
  }
};
File: webpack.config.babel.js

Now create a new directory called webpack and then inside other called configuration, and then we need to create a file for each node of our Webpack configuration and export it. For example, let's start by creating a file for our node module, so you should call it as module.js:

export default {
  rules: [
    {
      test: /.(js|jsx)$/,
      exclude: /node_modules/,
      use: 'babel-loader'
    }
  ]
};
File: webpack/configuration/module.js

Let's create now a file for the plugins:

import HtmlWebPackPlugin from 'html-webpack-plugin';

const plugins = [
  new HtmlWebPackPlugin({
    title: 'DEV Education',
    template: './src/index.html',
    filename: './index.html'
  })
];

export default plugins;
File: webpack/configuration/plugins.js

It's very useful to add our array of plugins into a constant because then we can add more plugins based on the environment (development or production), so now you can add plugins in a conditional way using push. Now the last file is for our resolve:

export default {
  extensions: ['.js', '.jsx']
};
File: webpack/configuration/resolve.js

We can import our files directly, but I prefer to use an index.js file and export all of them. 

// Configuration
import module from './module';
import plugins from './plugins';
import resolve from './resolve';

export {
  module,
  plugins,
  resolve
};
File: webpack/configuration/index.js

Now our webpack.config.babel.js will be very clean: 

Only members can see all the codes
You can Login or Sign Up

File: webpack.config.babel.js

If you liked this post and you want to learn more about React and Webpack then you should get my React Cookbook.

avatarLeave a comment

Your comment

Only members can comment. You can Login or Sign Up