I installed some npm modules that sounded sensible, used Gulp as my task runner as I normally like doing, and… nothing worked. I ended up spending a whole afternoon trying to set up my project, and never got round to actually writing any code. Why was this so hard?
Based on anecdotal evidence from colleagues and friends, I know that I’m not the only one feeling this frustration. And I assume even the developers at Facebook must have experienced similar afternoons of banging their heads on their desks, because they have kindly created an open source React project starter kit.
So, I decided to do some detective work in an attempt to find the most minimalist setup for a React project possible, and to really understand what each of the necessary dependencies does.
Because surely it can’t be that hard? Surely there must be a minimal setup with which you can write a React app?
ES5 or ES6?
However, it is much, much more convenient to use ES6 with React. Imports are easier, extending the React class is simpler, function declarations are shorter, and more. There’s a great blog post on the npmjs website that compares React with ES5 and ES6 using loads of examples that illustrate the advantages of using ES6. Another argument for using ES6 is that most of the documentation and examples you find online use ES6.
Let’s start with the most obvious dependency: React!
But behold: React is not enough.
There are two packages that you need for a React web application:
That was my first source of confusion.
I just wanted to write some React—why do I need two dependencies?
Turns out that these packages used to be combined, but
react-dom was split out about a year ago.
The reason is that React can be used for applications other than websites (React Native, anyone?), and rather than having one big package for all purposes, they were separated out so you can pick and choose exactly what you need.
A bundler can use lots of different plugins to transform our source code. You just need to specify which plugins you want to use, which files should be processed by those plugins, and where to put the final product after the processing is finished.
Most React tutorials suggest webpack, but I’ve also worked with Browserify.
Those two bundlers have slightly different philosophies:
Browserify came first and was built to run Node.js code in the browser.
webpack was created later, and with the primary focus of managing static assets for the front-end.
Browserify is more modular, while webpack comes with more features out of the box.
Browserify is driven by convention, while webpack is a lot more flexible, which makes it a bit harder to learn.
The feature that ultimately sold me on webpack is hot module replacement.
It is used together with the very convenient webpack dev server, which watches your files for any changes and reloads the page automatically.
With hot module replacement, it will only reload the section of a page that is affected by any changes to a module.
The advantage of this over refreshing the whole page is that all the other components will keep their state.
command + shift + R repetitive strain injury, plus you don't have to potentially perform lots of user actions to get all your components back into a particular state.
However, this blog post is about the most minimal setup, and you can definitely use webpack without the dev server and hot module reloading. So, I won't include these features into the setup for now.
As a side note—there is a bit of a difference between task runners such as Gulp and Grunt and bundlers like webpack and Browserify. Generally, task runners are more concerned with overall automation like building your project, getting it ready for deployment, or running your tests. And bundlers are for concatenating all your files and translating your fancy frameworks into vanilla ES5. There is quite a bit of overlap of responsibilities though. It is possible to use webpack in combination with Gulp or Grunt, but a lot of people just use npm scripts to complete the tasks that webpack doesn’t handle.
Babel can help us out here.
We can plug it into webpack as a loader using the npm package called
But once again, the Babel loader is not enough.
We have to explicitly install the peer dependencies.
To get all the functionality we require, we need three additional npm packages:
But that is really all we need to write a React application!
Step-by-step guide to get started
Let’s get started with the setup! You need to have Node installed to run all these commands. I use Node Version Manager to manage my versions but it’s not a must.
Let’s initialise a new project:
Just hit enter to get all the default values filled in automatically.
You can always change them later.
package.json file has now been created for us.
And now comes a big, long command to install all the dependencies from our shopping list.
If we look at our
package.json file we can see that all these dependencies have now been added under
devDependencies. And they have also been installed in your
package.json file is a way for your application to remember all the dependencies it has. So you never need to commit your
/node_modules/ directory because you can just run
npm install, which will look at your
package.json file to know which node modules it needs to install.
Next we need to create two folders—one for our source code and one for the processed version of the code that webpack will spit out. Let’s say the names for the folders are
dist/ (i.e. source and distribution).
And now we need a file into which we can write our React code.
It should live inside the
And let’s not forget the
index.html, which does not need to be transpiled, so we can put it directly into the
If you were following the commands, continue with:
Now let’s write some code!
index.html, add the standard HTML boilerplate code and then, inside the body, add an element with an id and a script tag.
The script that we’re referencing here does not exist yet, but once we’ve got some React code, we will let webpack do its magic and automatically output a
bundle.js file with our transpiled code.
And the React code will go into
And now let’s get to the pièce de résistance of our whole setup: the webpack config file!
As the bare minimum, it needs to export an object with three properties: entry, output, and loaders.
- entry specifies the entry path to your application, which in our case is the
- output specifies the folder into which webpack will place the automatically generated file with the transpiled code
- loaders is an array of tasks that webpack will carry out
Here is what the config file looks like:
test property in the loader object has nothing to do with setting up unit test frameworks, but is related to the unix
test key's value specifies for which files the loader should be responsible.
So when it goes through all the files, it first “tests” if it is the correct file before applying the processing.
A note about the output path: You can hardcode the path, but it is better practice to use Node.js’s path module. The path module's default operation varies based on the operating system that you are using to run the code. Any differences between Windows and Mac will be handled by this module, so that's one less thing for us to worry about.
As the last step, we need to tell the Babel loader that we want to use it to compile ES6 to ES5 and React to vanilla JS. We need to create a .babelrc for that with the following specifications.
The last step is to add the webpack command to our package.json.
scripts object and add another key-value pair under the existing
To transpile and bundle the code, we use the command:
And now we can open the
index.html in the browser and see our “Hello World” headline.
I really do recommend installing the webpack-dev-server. It is just so convenient! Let’s install webpack as a development dependency:
Now we can add the command to run the server to our package.json.
Underneath the build command inside the
scripts object, add another key-value pair.
Let’s call the command
To start the server, run
...and your code will run on localhost. Now make a change in your React Main module. See what I mean? Amazing!