Using create-react-app with React Router + Express.js

EDIT: This guide is outdated because it was made before react-router v4.

I’m writing this guide because I haven’t found implementations of this setup using the new and cool (and official) create-react-app by Facebook.

This is not about server-side rendering or Redux.

The source code is available here: https://github.com/mrpatiwi/routed-react

Why this is important?

Suppose you want to start a new project with React.js and because you are a good software developer, you want to use npm, babel and webpack.

Where to start?

Before create-react-app you had to search and try a infinite amount of boilerplates and example repositories to finally had a mediocre setup after two days of wasted time.

Now it’s easier :)

Getting started

I assume you already have Node.js and npm installed. So we begin with the cli:

npm install -g create-react-app

And let’s create an app named routed-react:

create-react-app routed-react
cd routed-react

To start or application at http://localhost:3000/ we run:

npm start

Refactor project structure

Considering that the project eventually will grow. Let’s make some changes to the directory structure.

I find a good practice to have reusable components with modificable styles.

Always consider composing className props and passing the remaining props to the first component. I recommend using classnames library to do so:

npm install --save classnames

Create a components directory:

# Create 'components' directory and 'App' sub-directory
mkdir -p src/components/App
# Move App component to new directory
mv src/App.js src/components/App/index.js
mv src/App.css src/components/App/style.css
mv src/logo.svg src/components/App/logo.svg

And modify the App component:

Now start an About component that will be shown at /about

mkdir src/components/About
touch src/components/About/index.js
touch src/components/About/style.css

A sample About component:

Reusability FTW!

Finally a NotFound component for all those 404's:

mkdir src/components/NotFound
touch src/components/NotFound/index.js
touch src/components/NotFound/style.css

At this moment the app should be crashing, don’t worry about that. We are fixing it right now.

Router setup

Let’s begin with react-router. We also want to use browserHistory strategy. The official docs will explain what it does better than me:

Browser history is the recommended history for browser application with React Router. It uses the History API built into the browser to manipulate the URL, creating real URLs that look like example.com/some/path.

Just install it:

npm install --save react-router

Now we must define our routes:

And the main index.js now looks like:

So much better :)

If we visit http://localhost:3000/about we should see (any other route will show our 404 view):

Usage with Express.js

So we need to serve our app. Github pages or similar will probably won’t work as expected unless you host the app on the root domain. Even if you do that, will only navigate as expected if the first visited page is the root.

Our option is to use a own hosting and always serve the main index.html for every possible route (that doesn’t override the access to the other static files as .js, .css and .favicon).

# Install dependencies
npm install --save express morgan
# Create server files
mkdir server
touch server/app.js
touch server/index.js

To see if everything is working well, build the app and start the server:

# Build to 'build' directory (it's ignored by git, see .gitignore)
npm run build
# Start the express.js app
node server

Visit http://localhost:9000/about and everything should be working fine.

Testing

Only for projects created with create-react-app 0.3.0 or lower.

Do you want testing? Ok testing, but focused in unit-testing the serving of assets of our Express.js app.

# Install dependencies
npm install --save-dev mocha chai supertest supertest-as-promised mz
# Create test directory
mkdir test
touch test/server.test.js

mz is a library to modernize some Node.js native functions.

We love promises, right?

Remember to modify the scripts from package.json:

"scripts": {
"start": "react-scripts start",
"start:server": "node server",
"build": "react-scripts build",
"eject": "react-scripts eject",
"test": "mocha test"
},

Basically we expect that:

Run the test suite with npm test ☕️

$ npm test> routed-react@0.0.1 test /Users/patriciolopez/Repositories/routed-react
> mocha test
builds application
✓ builds to "build" directory (9017ms)
express serving
✓ responds to / with the index.html (38ms)
✓ responds to favicon.icon request
✓ responds to any route with the index.html
4 passing (9s)

You can see that building the React.js app takes a lot of time, but this is a expected behavior because behind the scenes some unused code is removed and the rest if transpiled, optimized, uglificated and bundled in a few lightweight files.

Oh! wait!

1:1   warning  'use strict' is unnecessary ...  strict
9:1 warning 'describe' is not defined no-undef
10:3 warning 'it' is not defined no-undef
19:1 warning 'describe' is not defined no-undef
20:3 warning 'it' is not defined no-undef
28:3 warning 'it' is not defined no-undef
35:3 warning 'it' is not defined no-undef

Customize ESLint

You should be using ESLint and a IDE plugin like atom-eslint or similar (depending on your favorite development environment).

create-react-app comes with a default config for it. You can override this settings within eslintConfig at your main package.json file. So to disable the test suit warnings I recommend to edit it as follow:

"eslintConfig": {
"extends": "./node_modules/react-scripts/config/eslint.js",
"env": {
"browser": true,
"mocha": true,
"node": true
},
"rules": {
"strict": 0
}
}

See http://eslint.org/docs/user-guide/configuring for more customizing information.

Docker

Finally, let’s use the best deployment tool ever 🐳

touch Dockerfile

Write the following (it’s based on the officials Node.js docs):

Notice that we build our app inside the Docker image build step. Finally, when the container is started it should execute npm run start:server and start the Express.js app at port 9000.

Running with Docker

Be sure to install Docker and start a Docker-machine if necessary. Let’s create an image named routed-react:

docker build -t routed-react .

Finally, start a container named routed-react-instance at port 80.

docker run -p 80:9000 --name routed-react-instance routed-react

Patricio López Juri

Written by

Software Engineer from Chile. Vinyl record collector.

Welcome to a place where words matter. On Medium, smart voices and original ideas take center stage - with no ads in sight. Watch
Follow all the topics you care about, and we’ll deliver the best stories for you to your homepage and inbox. Explore
Get unlimited access to the best stories on Medium — and support writers while you’re at it. Just $5/month. Upgrade