Use React-Router with Ease
We will be using a sample app that I created in one of my previous posts. Click here to get the sample app or read Simplest React App Setup to do it yourself.
Introduction
According to the docs,
React Router is a complete routing library for React. React Router keeps your UI in sync with the URL. It has a simple API with powerful features like lazy code loading, dynamic route matching, and location transition handling built right in. Make the URL your first thought, not an after-thought.
Setting up React routing is only needed for applications that have more than one page. This post is about the basic set up. In my next post, we will take a deeper look into react-routing.
Set Up
If you followed the instruction above, you should have a sample app set up.
To start with, let’s install react-router.
Run
npm install --save react-router
Currently, our app is a single page app, so let's create two components: About.js
& AppContainer.js
. About.js
will be the component for our about page while AppContainer.js
will contain the generic content that all pages need to have. For example, the navbar.
Run
touch components/About.js components/AppContainer.js
copy and paste this into About.js
.
import React from 'react'
const About = () => <h1>This is an about page</h1>
export default About
also, copy and paste the code below into AppContainer.js
import React from 'react'
import { Link } from 'react-router'
const AppContainer = props => {
return (
<div>
<Link to='/'>Home</Link>
{ ' | ' }
<Link to='/about'>About</Link>
{props.children}
</div>
)
}
export default AppContainer
Next, let’s create a routes.js
file.
Run
touch routes.js
then copy and paste the code below in it.
import React, { Component } from 'react'
import { Route, IndexRoute } from 'react-router'
import HelloWorld from './components/HelloWorld'
import About from './components/About'
import AppContainer from './components/AppContainer'
export default (
<Route path='/' component={AppContainer}>
<IndexRoute component={HelloWorld} />
<Route path='/about' component={About} />
</Route>
)
The next thing is to update the app.js
file to reference routes.js
and no longer HelloWorld.js
via Router component from react-router.
Delete the code in app.js
then copy and paste this
import React from 'react'
import ReactDOM from 'react-dom'
import { Router, hashHistory, browserHistory } from 'react-router'
import routes from './routes'
ReactDOM.render(<Router routes={routes} history={hashHistory} />, document.getElementById('app'))
Router has a property called history. React-router provides two different kinds of history that can be passed into it. Most setup favour browserHistory
over hashHistory
because hashHistor
y is not SEO friendly and its URLs are ugly.
Now, let's start our server and see what we have.
Run
npm install && npm start
Now, if you navigate to the app on the browser, you should see HelloWorld component. Also, when you click the About link, you should see the About component.
If you would like a cleaner SEO friendly URL, or you are using this in production, then browserHistory
is the way to go. You may want to read more about browserHistory
vs. hashHistory
.
Now, update the Router part of the code in App.js
like so
<Router routes={routes} history={browserHistory} />
Your app should still work as expected. You should be able to visit both pages.
If you're trying to visit the About component http://localhost:8080/about by pasting the route directly into the address bar or reloading the page, you will get
Cannot GET /about
When you're using browserHistory
, you must have a server that will always return your index.html
at any route. Handling this error is necessary because the users of your application might bookmark a link that is not the index route. For this tutorial, we will use an express server.
To set this up for node.js, run
npm install --save express path
then create a server.js
file
touch server.js
and paste the configuration below into it.
const express = require('express')
const path = require('path')
const port = process.env.PORT || 8080
const app = express()
// serve static assets normally
app.use(express.static(__dirname + '/public'))
// handle every other route with index.html, which will contain
// a script tag to your application's JavaScript file(s).
app.get('*', function (request, response){
response.sendFile(path.resolve(__dirname, 'public', 'index.html'))
})
app.listen(port)
console.log("server started on port " + port)
Check out this link to learn more on browserHistory
.
Update your webpack.config
file to look like this
var webpack = require('webpack')
var path = require('path')
module.exports = {
entry: path.resolve(__dirname, 'app'),
output: {
path: __dirname + '/public',
publicPath: '/',
filename: 'bundle.js'
},
module: {
loaders: [
{test: /\.js$/, exclude: /node_modules/, loaders: ['babel-loader']}
]
}
}
Note: We have removed the main settings for webpack-dev-server. It is no longer needed since we now have an express server. Visit here to learn more about webpack-dev-server.
You can start your app by running:
webpack && node server.js
Copying and pasting a route directly should work as expected now.
Since webpack-dev-server is no longer being used, any change we make on a file will not be noticed by webpack. For watching of files to continue, a configuration tweak is needed. Let’s install npm-run-all
— a module to run multiple commands on the terminal without necessarily waiting for one to finish.
Run
npm install --save npm-run-all
then let’s update our package.json
file. The scripts section should have the code below:
"scripts": {
"start": "npm-run-all --parallel webpack:build server",
"webpack:build": "webpack -w",
"server": "node server.js",
"test": "echo \"Error: no test specified\" && exit 1"
},
Your app can now be started with npm start .
If all went well, you should be able to visit http://localhost:8080/ and also http://localhost:8080/about.
Here's a detailed tutorial on how Server-Side Rendering with Redux and React-Router work!