Creating a React App… From Scratch.

Jedai Saboteur
8 min readApr 20, 2018

--

React doesn’t ‘just work’ out of the box. It uses keywords and syntax that node (as of v. 9.3.0, which we’re using for this tutorial) hasn’t quite caught up to just yet. It requires a fair bit of setup that can be cumbersome to figure out, and Facebook has provided an option that makes starting a React app easy. Why bother, right?

The thing is, create-react-app abstracts a lot of what makes a React app work away from you — at least without ejecting it and having to tweak all of the options by hand. There are a number of reasons you may want to make your own implementation, or at least have some idea of what it’s doing under the hood.

As I mentioned, there are a couple of hurdles to starting a React app. The first is that node can’t process all of the syntax (such as import/export and jsx). The second is that you’ll either need to build your files or serve them somehow during development for your app to work — This is especially important in the latter situation.

Luckily, we can handle these issues with Babel and Webpack.

Setup

To get started, create a new directory for your React app. Then, initialize your project with npm init and open it in an editor of your choice. It’s also a pretty good time to git init as well. In your new project folder, create the following structure:

.
+-- public
+-- src

Thinking ahead a little bit, we’ll eventually want to build our app and we’ll probably want to exclude the built version and our node modules from commits, so let’s go ahead and add a .gitignore file excluding (at least) the directories node_modules and dist.

Our public directory will handle any static assets, and most importantly houses our index.html file, which react will utilize to render your app. The following code was sourced from the react documentation with some very slight modifications. Feel free to copy the following HTML markup into a new file index.html inside of the public directory.

The most important lines to pay attention to are 10, which is the root our React app will hook into, and line 14, which references our (soon to be) built react app. You can name your built script whatever you like, but I’ll be using bundle.js for this tutorial.

Now that we’ve got our HTML page set up, we can start getting serious. We’re going to need to set up a few more things. First, we need to make sure the code we write can be compiled, so we’ll need Babel.

Babel

Go ahead and run npm install --save-dev @babel/core@7.1.0 @babel/cli@7.1.0 @babel/preset-env@7.1.0 @babel/preset-react@7.0.0.

babel-core is the main babel package — We need this for babel to do any transformations on our code. babel-cli allows you to compile files from the command line. preset-react and preset-env are both presets that transform specific flavors of code — in this case, the env preset allows us to transform ES6+ into more traditional javascript and the react preset does the same, but with JSX instead.

In the project root, create a file called .babelrc. Here, we’re telling babel that we’re going to use the env and react presets.

Babel also has a ton of plugins available that can be used if you only need to transform specific features or some feature you need isn’t covered by env. We won’t worry about those for now, but you can check them out here.

Webpack

Now we need to acquire and configure Webpack. We’ll need a few more packages, and you’ll want to save these as dev dependencies: npm install --save-dev webpack@4.19.1 webpack-cli@3.1.1 webpack-dev-server@3.1.8 style-loader@0.23.0 css-loader@1.0.0 babel-loader@8.0.2.

Webpack uses loaders to process different types of files for bundling. It also works easily alongside the development server that we’re going to use to serve our React project in development and reload browser pages on (saved) changes to our React components. In order to utilize any of this though, we’ll need to configure Webpack to use our loaders and prepare the dev server.

Create a new file at the root of the project called webpack.config.js. This file exports an object with webpack’s configuration.

Let’s take a quick walk through this: entry (line 5) tells Webpack where our application starts and where to start bundling our files. The following line lets webpack know we’re working in development mode — This saves us from having to add a mode flag when we run the development server.

The module object helps define how your exported javascript modules are transformed and which ones are included according to the given array of rules.

Our first rule is all about transforming our ES6 and JSX syntax. The test and exclude properties are conditions to match file against. In this case, it’ll match anything outside of the node_modules and bower_components directories. Since we’ll be transforming our .js and .jsx files as well, we’ll need to direct Webpack to use Babel. Finally, we specify that we want to use the env preset in options.

The next rule is for processing CSS. Since we’re not pre-or-post-processing our CSS, we just need to make sure to add style-loader and css-loader to the use property. css-loader requires style-loader in order to work. loader is a shorthand for the use property, when only one loader is being utilized.

The resolve property allows us to specify which extensions Webpack will resolve — this allows us to import modules without needing to add their extensions.

The output property tells Webpack where to put our bundled code. The publicPath property specifies what directory the bundle should go in, and also tells webpack-dev-server where to serve files from.

The publicPath property is a special property that helps us with our dev-server. It specifies the public URL of the the directory — at least as far as webpack-dev-server will know or care. If this is set incorrectly, you’ll get 404’s as the server won’t be serving your files from the correct location!

We set up webpack-dev-server in the devServer property. This doesn’t require much for our needs — just the location we’re serving static files from (such as our index.html) and the port we want to run the server on. Note that devServer also has a publicPath property. This publicPath tells the server where our bundled code actually is.

That last bit might have been a little confusing — Pay really close attention here: output.publicPath and devServer.publicPath are different. Read both entries. Twice.

Finally, since we want to use Hot Module Replacement so we don’t have to constantly refresh to see our changes. All we do for that in terms of this file is instantiate a new instance of the plugin in the plugins property and make sure that we set hotOnly to true in devServer. We still need to set up one more thing in React before HMR works, though.

We’re done with the heavy setup. Now let’s get React working!

React

First, we’ll need to get two more packages: react@16.5.2 and react-dom@16.5.2. Go ahead and save those as regular dependencies.

We’ll need to tell our React app where to hook into the DOM (in our index.html). Create a file called index.js in your src directory. This is a very small file that does a lot in terms of your React app. Check it out.

ReactDOM.render is the function that tells React what to render and where to render it — In this case, we’re rendering a component called App (which we’ll create soon), and it’s being rendered at the DOM element with the ID root (line 10 of index.html).

Now, create another file in src called App.js. If you’ve worked with React using create-react-app this part should be extremely familiar. This file is just a React component.

While we’re still here, I did mention that webpack also processes CSS (and we are requiring it our component). Let’s add a really simple stylsheet to the src directory.

Your final project structure should look like the following, unless you changed some names along the way:

.
+-- public
| +-- index.html
+-- src
| +-- App.css
| +-- App.js
| +-- index.js
+-- .babelrc
+-- .gitignore
+-- package-lock.json
+-- package.json
+-- webpack.config.js

We now have a functioning react app! We can start our dev server by executing webpack-dev-server --mode development in the terminal. I would advise putting it in your start script in package.json to save yourself nine whole keystrokes.

Finishing HMR

If you run the server now, you’ll notice none of your changes actually cause anything to happen in the client. What gives?

Well, HMR needs to know what to actually replace and currently we haven’t given it anything. For that, we’re going to use a package one of the folks on the react team have provided us: react-hot-loader@4.3.11.

You can install this as regular dependency — as per the documentation

Note: You can safely install react-hot-loader as a regular dependency instead of a dev dependency as it automatically ensures it is not executed in production and the footprint is minimal.

Now, import react-hot-loader in App.js and mark the exported object as hot-reloaded by modifying to code as follows.

When you run your app now changes to the code should update the client immediately after saving.

Last Details

You might notice something interesting (or maybe startling) about starting your project: Built files never show up in your dist directory. See, webpack-dev-server is actually serving the bundled files from memory — once the server stops, they’re gone. To actually build your files, we’re going to utilize webpack proper — add a script called build in your package.json with the following command: webpack --mode development. You can replace development with production, but if you completely omit --mode, it will fall back to the latter and give you a warning.

That just about covers everything you need to be able to render a basic React app, without having to touch create-react-app. There’s still more to add to the implementation to make it more complete though — For instance, images aren’t set up to be processed by Webpack, but there’s a loader for that. I’ll leave that implementation to you. After all, if you don’t need or want to serve files, well, that’s just bloat, right?

I hope this article helped shine some light on what it takes to get a React app working, and how the basics work under the hood. I didn’t dive too deeply into details about Babel and Webpack, but please explore any of the myriad links peppered throughout the article or right into their documentation. They’re awesome tools that seem more intimidating at first glance than they really are and they can help take your development to the next level.

If you’re still unclear on any of that or want another reference, here’s the repository on github . You can also check out an older implementation (it goes a bit more in depth) or say hi on twitter.

This article was edited on April 24, 2018 to include package versions, May 13, 2018 to reflect a bugfix regarding webpack-dev-server, June 16, 2018 to reflect updates to React and other packages used throughout this guide, and September 23, 2018 for the same reason.

--

--

Jedai Saboteur

Software Engineer / MERN Enthusiast / Layabout Scoundrel