Why you should create your own Webpack configuration for React and how to do it
Published: 2020-07-16 20:08:33


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](/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](https://github.com/maximomartinezsoria/webpack-boilerplate) if you have a good understanding of how Webpack works.

Here you have the repository of this post: [react-webpack-configuration](https://github.com/maximomartinezsoria/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.

```bash
$ npm i react react-dom
```

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

```jsx
// 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.

```jsx
// 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.

```bash
$ npm i -D @babel/preset-react
```

Then, just add it to the `presets` array.

```jsx
// .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.

```jsx
// 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.

```jsx
// 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

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`.

```jsx
// 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.

```jsx
// 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.

```bash
$ 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:

- [Lazy load components](https://reactjs.org/docs/react-api.html#reactlazy).
- [Suspense component](https://reactjs.org/docs/react-api.html#suspense).
- PWA

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.