Using create-react-app with React Router + Express.js
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 adding style and className props. 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 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 if 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 helmet
# 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.
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 mz
# Create test directory
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
>[email protected] test /Users/patriciolopez/Repositories/routed-react
> mocha test
✓ builds to "build" directory (9017ms)
✓ 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.
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.