React Router is a third party library created to solve the problem of routing in React app. It wraps around the browser history API and does the job of keeping your React application UI in sync with the browser’s URL.
There are two packages of React Router: react-router-dom
for React and react-router-native
for React Native. Since you’re learning about making web application, you only have to install react-router-dom
:
npm install react-router-dom
There are 3 basic React Router components commonly used in minimal navigation, they are BrowserRouter
, Route
and Link
. Let’s explore about BrowserRouter
and Route
first:
import { BrowserRouter as Router, Route } from 'react-router-dom'
class RouterNavigationSample extends React.Component {
render() {
return (
<Router>
<>
<NavigationComponent />
<Route exact path="/" component={Home} />
<Route path="/about" component={About} />
</>
</Router>
)
}
}
The BrowserRouter
, which is imported as Router
, acts as the parent component that wraps all of your React component. It will intercept Browser request URL and match its path with the corresponding Route
component. So if the browser URL is localhost:3000/about
, the Router
will take that information and than look for a Route
component that has the path
attribute of /about
.
You will determine what will be rendered by adding the component
attribute to Route
.
In the sample above, an exact
attribute is added to the default Route
path (/
), because without it, any route with /
will also render the Home
component, causing inconsistencies in the navigation.
The third component Link
is used for navigation, replacing the regular <a>
tag of HTML. This is because a regular HTML anchor tag will do a full refresh of the browser on click, which is not suited for React application. A React app only needs to update the URL, browser history and component rendered without any browser refresh:
import { Link } from "react-router-dom";
class NavigationComponent extends React.Component {
render() {
return (
<>
<ul>
<li>
<Link to="/">Home</Link>
</li>
<li>
<Link to="/about">About page</Link>
</li>
</ul>
<hr />
</>
);
}
}
You can try a working demo here.
Note how you can use the previous and next button of the browser’s navigation bar, and the url is updated with subsequent navigation, without the browser loading. This is the power of React Router in action.
Making dynamic routing
You’ve seen how to create simple navigation using React Router, yet most web application require more advanced function than that. You probably need a dynamic routing, where you can put something like /user/:id
, in which React needs to render something based on the value of :id
.
Old links can also be dead and need to be redirected to new link.
Also, if the browser URL doesn’t match any existing route, you need to display a 404 page.
That’s why you need to learn about 2 more components, Switch
and Redirect
. Switch
is a unique component that will render the first matching Route
, then stop. To illustrate this example:
import { Route } from 'react-router'
<Route path="/about" component={About}/>
<Route path="/:user" component={User}/>
<Route component={NoMatch}/>
In the above code, a browser URL of /about
will match all three routes, causing it all to be rendered and stacked below each other. Now by using the Switch
component, React Router will render the About
component route and then stop.
import {Switch, Route} from 'react-router';
<Switch>
<Route path='/about' component={About} />
<Route path='/:user' component={User} />
<Route component={NoMatch} />
</Switch>;
The order of the Route
component inside Switch
is important, so make sure you declare all static route first before declaring routes with url parameter and 404 route.
Now for Redirect
, the component is pretty simple. You only need to add from
attribute that states the old URL and to
attribute specifying the new URL to link to.
import {Redirect} from 'react-router';
<Redirect from='/old-match' to='/will-match' />;
Nested route
In order to create nested route, you need to declare another Route
inside the parent component. For example, let’s say you have /users
route that render to Users component.
Let’s do a little exercise. First, create an array of objects that store user data, the following will do:
const users = [
{
id: '1',
name: 'Nathan',
role: 'Web Developer',
},
{
id: '2',
name: 'Johnson',
role: 'React Developer',
},
{
id: '3',
name: 'Alex',
role: 'Ruby Developer',
},
];
Now create a simple routing in the application:
class RouterNavigationSample extends React.Component {
render() {
return (
<Router>
<>
<NavigationComponent />
<Route exact path='/' component={Home} />
<Route path='/about' component={About} />
<Route path='/users' component={Users} />
</>
</Router>
);
}
}
The NavigationComponent is where you write the Link
component for navigating the application:
class NavigationComponent extends React.Component {
render() {
return (
<>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
<li>
<Link to='/about'>About page</Link>
</li>
<li>
<Link to='/users'>Users page</Link>
</li>
</ul>
<hr />
</>
);
}
}
It’s time to create components to render on specific routes. Home
and About
component will render a single div, while Users
will have another Link
and Route
component.
Inside the Users component, you will render a list of users, with a nested route to individual user by its ID, like /users/:id
:
const Home = () => {
return <div>This is the home page</div>;
};
const About = () => {
return <div>This is the about page</div>;
};
const Users = () => {
return (
<>
<ul>
{users.map(({name, id}) => (
<li key={id}>
<Link to={`/users/${id}`}>{name}</Link>
</li>
))}
</ul>
<Route path='/users/:id' component={User} />
<hr />
</>
);
};
There’s nothing new with this code. So you can write the User
component now:
const User = ({match}) => {
const user = users.find((user) => user.id === match.params.id);
return (
<div>
Hello! I'm {user.name} and I'm a {user.role}
</div>
);
};
Now here is something new I haven’t told you about. Every time a component is rendered in a specific route, the component receive route props from React Router. There are 3 route props being passed down into component: match
, location
, history
.
You can look at the props by opening the React Developer Tools and highlight the matching component route:
(If you’re opening from Codesandbox, you can open the demo in a new separate window to enable React DevTool)
Notice how you add /:id
URL parameter in the Users
component nested route. This id is passed down to the User
component through the match.params.id
object property. If you passed the URL parameter as /:userId
, it will be passed down as match.params.userId
.
Now that you know about route props, let’s refactor Users
component a bit:
const Users = ({ match }) => {
return (
<>
<ul>
{users.map(({ name, id }) => (
<li key={id}>
<Link to={`${match.url}/${id}`}>{name}</Link>
</li>
))}
</ul>
<Route path={`${match.url}/:id`} component={User} />
<hr />
</>
);
}
As always, here is a working demo.
Passing props to Route component
You might think that passing props into Route component is the same as passing into regular component:
<Route path="/about" component={About} user='Jelly'/>
Unfortunately, React Router doesn’t forward the props entered into Route
component into the component
props, so you have to use another method.
Fortunately, React Router provides a render attribute that accepts a function to be called when the URL locations matches. This props also receives the same route props
as the component
props:
<Route
path="/about"
render={props => <About {...props} admin="Bean" />}
/>
// the component
const About = props => {
return <div>This is the about page {props.admin}</div>;
};
First, you take the given props
from React Router and pass it into the component, so that the component can use match
, location
or history
props if necessary. Then you add your own extra props into it. The example above use arbitrary admin
props as example.
You can see the full code here.
Now you’ve learned about React Router, try to implement it for your React application!