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?
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 :)
npm install -g create-react-app
And let’s create an app named routed-react:
To start or application at http://localhost:3000/ we run:
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
A sample About component:
Finally a NotFound component for all those 404's:
At this moment the app should be crashing, don’t worry about that. We are fixing it right now.
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:
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
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
Visit http://localhost:9000/about and everything should be working fine.
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.
- Supertest (as promised)
# Install dependencies
npm install --save-dev mocha chai supertest supertest-as-promised mz# Create test directory
mz is a library to modernize some Node.js native functions.
We love promises, right?
Remember to modify the scripts from package.json:
"start": "react-scripts start",
"start:server": "node server",
"build": "react-scripts build",
"eject": "react-scripts eject",
"test": "mocha test"
Basically we expect that:
- The React.js app builds successfully
- The Express.js server send us the index.html file for any route
- The Express.js server send us the assets requested by the browser.
Run the test suite with npm test ☕️
$ npm test> firstname.lastname@example.org test /Users/patriciolopez/Repositories/routed-react
> mocha testbuilds 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.html4 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.
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
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:
See http://eslint.org/docs/user-guide/configuring for more customizing information.
Finally, let’s use the best deployment tool ever 🐳
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