✏ React Router
The react-router package makes it easy to add a navigation to a SPA. In a conventional navigation with <a>
tags, each click on the anchor will load a different HTML page. With react-router (more specifially, react-router-dom, which is for applications that run in the browser), a link doesn't lead to a different page, but to a different view, while at the same time making sure that each view has a different URL.
Installing to get started:
npm i react-router-dom
✏ Link
The App component will render the Navigation, and either a Home view, an About view, or a Contact view.
In the Navigation component, all <a>
tags are replaced by the imported Link. Instead of an href
attribute, it takes a to
prop to specify the path:
import { Link } from 'react-router-dom';
function Navigation() {
return (
<nav>
<Link to="/">Home</Link>
<Link to="/about">About</Link>
<Link to="/contact">Contact</Link>
</nav>
);
}
✏ Router and Route
Now wrap the whole App with the Router (for brevity, change the name from "BrowserRouter" to "Router" on import):
import { BrowserRouter as Router, Route } from 'react-router-dom';
import Navigation from './components/Navigation.js';
import HomeView from './views/HomeView.js';
import AboutView from './views/AboutView.js';
import ContactView from './views/ContactView.js';
function App() {
return (
<Router>
<Navigation />
<Route path="/" exact component={HomeView} />
<Route path="/about" component={AboutView} />
<Route path="/contact" component={ContactView} />
</Router>
);
}
The Route's path
corresponds to the Link's to
. The keyword "exact" in the Home Route takes care of a little quirk: The Router tries to match the to
prop against a path
, and "/"
is considered a match for all three Routes, because "/"
, "/about"
and "/contact"
all include the slash for the Home Route.
To avoid that, you can either
use the "exact" keyword
add a Switch and put the Home Route at the bottom of the list
✏ Switch
import { BrowserRouter as Router, Route, Switch } from 'react-router-dom';
function App() {
return (
<Router>
<Switch>
<Navigation />
<Route path="/about" component={AboutView} />
<Route path="/contact" component={ContactView} />
<Route path="/" component={HomeView} />
</Switch>
</Router>
);
}
This works because the Switch makes sure that only one view is rendered (the first match).
✏ Passing props
The Routes in the above example are self-closing, and use the component
prop to render a view. While this has certain advantages (see below), it's often required to pass some props into a view. This can be done by wrapping the View with the Route:
function App() {
return (
<Router>
<Navigation />
<Route path="/" exact ><HomeView props={props} /></Route>
<Route path="/about" ><AboutView props={props} /></Route>
<Route path="/contact" ><ContactView props={props} /></Route>
</Router>
);
}
✏ History
To make sure that the back and forward buttons of the browser still work as expected, the Router provides a history
object. If you write the Route as self-closing tag with the component
prop, the Router will automatically pass down some props, including the history
. You can access it through props.history
or by destructuring props:
function HomeView({ history }) {
return (
<div className="home"></div>
);
}
This object has some useful properties and methods, some of which are:
history.location.pathname
: the current path (the Route'spath
prop)history.goBack()
andhistory.goForward()
: self-explanatoryhistory.push(pathname)
: move to a different location (for example - after successfully logging in, you can forward a user to their profile screen by pushing/profile
)
(to see how to access this object in components that are wrapped by a Route, see below)
✏ Match
The match
object is created when a path
and the location (url) are successfully matched. You can access it in the same way that you can access the history
object above. It has the following properties:
match.path
andmatch.url
: the two strings that were matched against each othermatch.isExact
: Boolean to indicate whether it was an exact matchmatch.params
: object with key/value pairs, parsed from the URL (see below)
✏ Params
These are particularly useful when you have a lot of dynamic routes, a catalogue of products for example.
To link to a product:
<Link to=`/products/${productId}`>Awesome Product</Link>
The route to the product view (the id comes after a colon):
<Route path="/products/:productID" component={ProductView} />
Then in ProductView.js, you get access to the id via match.params
:
match.params // {productID: productId}
✏ useHistory, useLocation, useParams
When you nested the view component inside the Route instead of using the component
prop, the above mentioned objects won't be implicitly passed down for you, but you can use a few hooks instead to get access to them. To get specific properties, use destructuring:
import { useParams, useHistory, useLocation } from 'react-router-dom';
function ProductView(){
const params = useParams(); // the params object
const { productID } = useParams() // productId (i.e., '12345')
const location = useLocation(); // the location object
const { pathname } = useLocation(); // '/products/12345'
const history = useHistory();
return (
<div className="about"></div>
)
}
✏ Resources
React Router v5: The Complete Guide
✏ Recap
This post covered:
how to use React Router
Route and Link components
history, match and location objects
hooks to access these objects when they're not implicitly passed down
✏ Thanks for reading!
I do my best to thoroughly research the things I learn, but if you find any errors or have additions, please leave a comment below, or @ me on Twitter. If you liked this post, I invite you to subscribe to my newsletter. Until next time 👋
✏ Previous Posts
You can find an overview of all previous posts with tags and tag search here: