Maximo Martinez Soria
Published on

Why you should create your own Webpack configuration for React and how to do it

We all know create-react-app and how easily we can get a React project configuration done. However, when you need some more project-specific stuff, you would definitely like to know how to change or add things.

On the other hand, it's always a good idea to understand what's happening under the hood.

This is the second part of Let's create a Webpack configuration from scratch and I'll be using the files created on that post. So go and check it out before continue reading or just clone the repo if you have a good understanding of how Webpack works.

Here you have the repository of this post: react-webpack-configuration

Feel free to use it if you feel lost at some point.

Set Up

First of all, if we are going to code a React app, we'll need React.

$ npm i react react-dom

Also, let's create an entry point and a first component. Just to have something to test with.

// src/index.js
import './sass/index.scss' // remember to import your styles in this file
import React from 'react'
import { render } from 'react-dom'
import App from './components/App'

render(<App />, document.querySelector('#app'))

Let's create a hello world.

// src/components/App.js
import React from 'react'

const App = () => <p>Hello World!</p>

export default App

Now we are ready to go.

Babel for React

We already have some babel configuration in our .babelrc file. But it's not enough for React.

We'll need one more package.

$ npm i -D @babel/preset-react

Then, just add it to the presets array.

// .babelrc
{
    "plugins": [
        "@babel/plugin-transform-runtime"
    ],
    "presets": [
        "@babel/preset-env",
        "@babel/preset-react"
    ]
}

Development

As you know for the previous post, we're using two different files to take apart development and production configurations.

We've done quite a good job last time. But we need to improve something for React.

Hot Module Replacement

Hot Module Replacement, allows us to refresh just one part of the browser every time we save a file. This makes the developer experience delightful.

Let's do it.

// webpack.dev.config.js
...
const webpack = require('webpack')

module.exports = {
  ...
  devServer: {
    contentBase: path.resolve(__dirname, 'dist'),
    open: true,
    hot: true
  },
  ...
  plugins: [
    new webpack.HotModuleReplacementPlugin(),
    ...
  ],
}

webpack.HotModuleReplacementPlugin does quite all the job. Remember to import webpack and set hot to true in devServer object.

Finally, we'll need to make a little refactor in the entry point.

// src/index.js
import './styles/index.scss'
import React from 'react'
import { render } from 'react-dom'
import App from './components/App'

function renderApp() {
	render(<App />, document.querySelector('#app'))
}

renderApp()
if (module.hot) {
	module.hot.accept('./components/App', () => renderApp())
}

Encapsulate react render function into a custom function and then tell webpack to re-run that function every time we save the file.

Production

This is probably the worthiest part of making a custom configuration.

You can really improve performance and user experience with the following technique. However, there are a lot of things you could add here.

Dynamic link library is a set of libraries that hardly ever change. So we're going to pre-bundle them in order to avoid bundle them every time we run our build command. Also, the fact of having these libraries in a different file, will prevent the browser to download them over and over again.

First, we need to create a new webpack file. Yes, another one. This one is so simple and it's usually called webpack.dll.config.js.

// webpack.dll.config.js
const path = require('path')
const webpack = require('webpack')

module.exports = {
	entry: {
		modules: ['react', 'react-dom'],
	},
	output: {
		path: path.resolve(__dirname, 'dist'),
		filename: 'js/[name].dll.js',
		library: '[name]',
	},
	mode: 'production',
	plugins: [
		new webpack.DllPlugin({
			name: '[name]',
			path: path.join(__dirname, '[name]-manifest.json'),
		}),
	],
}

You might be able to understand almost everything. But there are a couple of weird things...

Why is the entry point receiving and array of strings? Well, those strings are the names of the libraries. And those libraries are the ones that we are compiling.

In this case we're only using react and react-dom because it's a very simple example. But go ahead and install react-router, redux or any other library that you want and add them to the modules array.

Output and mode are quite simple. We've been using them for a while and they don't have nothing new in this case.

Finally, we are using a new plugin. It is part of the Webpack core and it help us creating a manifest. This manifest, is going to tell Webpack not to re-compile this libraries that we've already compiled.

That next step, is the build command and in order to make that happen, we need to add a plugin.

// webpack.config.js
...
const webpack = require('webpack')

module.exports = {
	...
  plugins: [
    ...
		new CleanWebpackPlugin({
      cleanOnceBeforeBuildPatterns: ['**/main.*']
    }),
    new webpack.DllReferencePlugin({
      manifest: require('./modules-manifest.json')
    }),
		new AddAssetHtmlPlugin({
      filepath: path.resolve(__dirname, "dist/js/*.dll.js"),
      outputPath: "js",
      publicPath: "http://localhost:3001/js/"
    })
  ]
}

This one is again part of the Webpack core and it reads the output of DllPlugin and prevents Webpack from compiling our libraries again.

On the other hand, I've changed the pattern of CleanWebpackPlugin to avoid deleting our modules.dll.js file.

Finally, we're using a new plugin which links our modules.dll.js to our html file automatically.

$ npm i -D add-asset-html-webpack-plugin

React code and Webpack Docs

We've finished. But if you want to continue improving your user and development experience, I leave you a couple of things to look at.

You can make a lot of difference in user experience if you take care of your React code. These are a couple of React techniques related with performance:

PWAs could be complicated at the beginning but they're really useful and allow you to have awesome features like offline support, notifications, offline analytics, shortcut icon from smartphones and computers, etc.

Also, don't forget to check out Webpack's documentation. It's plenty of interesting options to improve either your experience or the users one.