Introduction
React is arguably one of the most approachable frameworks for building modern UIs. Something that splits developers on loving or hating React is the lack of ‘extras’ shipped by default. One of these things is a router for internally navigating in the application. Instead of including code you may not even use, React sticks more closely to YAGNI (you ain’t gonna need it) principles in this regard.
Luckily, for routing, there is a fairly simple solution. The React Router (‘react-router-dom’ package is used for nearly all web use-cases) is an efficient way to navigate your application. One of the biggest benefits of using React Router, is that it intercepts links, and only updates the necessary part of the page. This decreases load times, data transfers, and often alleviates the need for repeated HTTP calls. Because of these increases in efficiency, React Router is usually the preferred way to perform in-application routing.
Is React Router Necessary?
Unlike traditional “static” websites, frameworks like React are generally used to create SPAs, or “single-page applications.” SPAs create a bundle of code upfront to provide savings in both time and data-usage. This is usually more efficient than the traditional approach where each file is stored and routed to independently. While a SPA may end up loading some unnecessary code up front, back-and-forths between the browser and server are drastically reduced.
Routing can be accomplished using HTML <a> tag links, but this is usually less efficient. The problem with using <a> tags for routing to new pages is when clicked, they trigger an entire page load. This negates many of the benefits of a single-page-application. The React Router, alternatively, allows React to only update the parts of the page that need to change, making things much more efficient.
If you want to use React Router, there is a little setup work to do. This article will help you understand the basics of routing, how to add the necessary packages, and get routing set up in your React application.
Table of Contents
- Prerequisites
- Getting Started
- Creating A New React Project
- Adding React Router
- Routes
- Link
- Switch
- Redirect
- Passing Params
- Route Params
- Query Params
- Recap
- Resources
Prerequisites
These are the prerequisites for following along:
- Basic command-line skill
- A code editor
- Node installed
To install Node, visit nodejs.org
Getting Started
In order to understand React Router, we need a project to work with. If you already have a React project created, feel free to skip ahead to the Adding React Router section.
Creating A New React Project
The easiest way to start a new React project is with the create-react-app command. While you can install create-react-app globally, using npm i -g create-react-app, the newer npx create-react-app command (or yarn create react-app if you prefer yarn) avoids the need to install the package globally. (Keep in mind, in order to use npx commands, you will need to be using npm version 5.2 or higher.)
Use Of Typescript
There are many benefits to using Typescript. As such, this article uses the create-react-app typescript template. If you would like to opt-out of Typescript feel free to drop –template typescript from these commands.
Run one of these commands:
- Global Install: create-react-app learning-react-router –template typescript
- NPX: npx create-react-app learning-react-router –template typescript
- Yarn: yarn create create-react-app learning-react-router –template typescript
After you have a project, change into the project directory:
- cd learning-react-router
Adding React Router
Once you are in the project directory in your terminal, the last part of the setup is to make the router available in our project. To do that, we need to run one of these commands (leave off the @types section of this command if you are not using Typescript):
- NPM: npm install –save react-router-dom @types/react-router-dom
- Yarn: yarn add react-router-dom @types/react-router-dom
Normal Cleanup
There should usually be some clean up of the default create-react-app implementation, however, in the interest of saving time, we will be working with what was created.
More On Typescript
The file extensions in this article are .tsx because Typescript is used. If you are not, yours should be .jsx.
Replace your App.tsx with this code:
const App = () => { return ( <h1>Learning React Router!</h1> ); }
Make sure everything is working by running either npm start or yarn start.
If you followed the create-react-app section, you should be able to navigate to localhost:3000 and see “Learning React Router!” in the window.
Routes
With that basic setup out of the way, let’s get to the real heart of this article! The first thing to do is to create the components our Routes will point to.
Start by creating a folder called Routes in the root src folder. Inside the new Routes folder, we will create 2 files: Home.tsx and About.tsx. They should look like this:
Home.tsx
export const Home = () => { return <h1>Home!</h1>; };
About.tsx
export const About = () => { return <h1>About!</h1>; };
Now we need to change a few things in App.tsx in the src folder. Let’s import the Router and Route from ‘react-router-dom’.
BrowserRouter vs HashRouter
There are 2 main ways to accomplish routing: HashRouter and BrowserRouter. If you are targeting an older browser that doesn’t support the HTML5 History API, you’ll need to use HashRouter. This will add # into your route (localhost:3000/#/home). If you are targeting more recent browsers, BrowserRouter is much more elegant and avoids adding hashes into your route (localhost:3000/home).
It is common to alias these imports as Router.
We also need to import our two Route components (Home and About). Replace what is currently in App.tsx with this:
import { BrowserRouter as Router, Route } from 'react-router-dom'; import { Home } from './Routes/Home'; import { About } from './Routes/About'; const App = () => { return ( <> <h1>Learning React Router!</h1> <Router> <Route path="/about" component={About} /> <Route path="/" component={Home} /> </Router> </> ); }; export default App;
If your app is running, you may notice something strange. Both routes currently show on your page! That’s probably not what you were expecting! One way to avoid this is to add the exact attribute to each Route.
‘Exact’ Attribute
Without the exact attribute on your Routes, visiting the About route, will display the Home route as well. This is because /about is kind of like an extension of /, so both end up matching. The exact attribute adds a higher Route-matching scrutiny and makes it so /about does not match /. It is good practice to add the exact keyword unless there is a specific reason not to (say, a parent route needs to be visible as well as the child).
Add the exact attribute to each of your Routes like this:
<Route path="/about" exact component={About} /> <Route path="/" exact component={Home} />
With the app running, when you visit localhost:3000, you should see ‘Home!’ in the browser. Visit localhost:3000/about and you should see ‘About!’.
Congratulations! You have implemented basic routing! Next, let’s add some links to make this easier to use.
Routes
With that basic setup out of the way, let’s get to the real heart of this article! The first thing to do is to create the components our Routes will point to.
Start by creating a folder called Routes in the root src folder. Inside the new Routes folder, we will create 2 files: Home.tsx and About.tsx. They should look like this:
Home.tsx
export const Home = () => { return <h1>Home!</h1>; };
About.tsx
export const About = () => { return <h1>About!</h1>; };
Now we need to change a few things in App.tsx in the src folder. Let’s import the Router and Route from ‘react-router-dom’.
BrowserRouter vs HashRouter
There are 2 main ways to accomplish routing: HashRouter and BrowserRouter. If you are targeting an older browser that doesn’t support the HTML5 History API, you’ll need to use HashRouter. This will add # into your route (localhost:3000/#/home). If you are targeting more recent browsers, BrowserRouter is much more elegant and avoids adding hashes into your route (localhost:3000/home).
It is common to alias these imports as Router.
We also need to import our two Route components (Home and About). Replace what is currently in App.tsx with this:
import { BrowserRouter as Router, Route } from 'react-router-dom'; import { Home } from './Routes/Home'; import { About } from './Routes/About'; const App = () => { return ( <> <h1>Learning React Router!</h1> <Router> <Route path="/about" component={About} /> <Route path="/" component={Home} /> </Router> </> ); }; export default App;
If your app is running, you may notice something strange. Both routes currently show on your page! That’s probably not what you were expecting! One way to avoid this is to add the exact attribute to each Route.
‘Exact’ Attribute
Without the exact attribute on your Routes, visiting the About route, will display the Home route as well. This is because /about is kind of like an extension of /, so both end up matching. The exact attribute adds a higher Route-matching scrutiny and makes it so /about does not match /. It is good practice to add the exact keyword unless there is a specific reason not to (say, a parent route needs to be visible as well as the child).
Add the exact attribute to each of your Routes like this:
<Route path="/about" exact component={About} /> <Route path="/" exact component={Home} />
With the app running, when you visit localhost:3000, you should see ‘Home!’ in the browser. Visit localhost:3000/about and you should see ‘About!’.
Congratulations! You have implemented basic routing! Next, let’s add some links to make this easier to use.
Self-Closing Routes
In this article, ‘self-closing’ Route tags are used. ‘Self-closing’ means that the HTML element closes itself with a /, instead of relying on a separate closing HTML tag.
This:
<Route />
vs this:
<Route></Route>
If you have a component that takes params, you can use the non-self-closing syntax for Route as well. In the interest of simplicity, this article uses self-closing tags. Here is an example of that syntax:
<!-- Example of Non-Self-Closing Tag --> <Route path="/" exact> <Home /> </Route>
React Router Link
Links are a way to navigate in our app without the need to refresh the entire page. Let’s import Link from ‘react-router-dom’ and put some links in an unordered list to get this working!
Replace your App.tsx with this code:
import { BrowserRouter as Router, Route, Link } from 'react-router-dom'; import { Home } from './Routes/Home'; import { About } from './Routes/About'; const App = () => { return ( <> <h1>Learning React Router!</h1> <Router> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> </ul> <Route path="/" exact component={Home} /> <Route path="/about" exact component={About} /> </Router> </> ); }; export default App;
Now, when you visit localhost:3000 in your browser, click on the ‘About’ link, and you will see routing working without having to update the URL manually. Notice also that the entire page is not refreshing!
React Router Switch
Currently, if you remove the exact attribute from each of the routes, both the Home and About routes would show when you go to the About route. To see this, change the Route code to the following, and go back and forth between the Home and About links.
<Route path="/" component={Home} /> <Route path="/about" component={About} />
If you only want one of your routes to appear at a time, you can use a Switch. A Switch evaluates each Route in order and displays only the first one with a path value matching the current url. This avoids showing multiple matching Routes. While this is useful, it requires either use of the exact attribute, or more carefully planning the order you place your routes.
Adding a Switch around the Routes will ensure only one of these Routes is hit. However, if you leave your Routes as they are, you will notice that the About route never gets hit now! First add Switch to your ‘react-router-dom’ imports:
import { BrowserRouter as Router, Route, Link
<!-- Example of altering route order --> <Switch> <Route path="/about" component={About} /> <Route path="/" component={Home} /> </Switch>
‘Exact’ Attribute in a Switch
While there are definitely cases where carefully specifying the order is useful or required, explicit use of the exact keyword is preferred when possible.
To recap, use Switch if you only want one of the Routes to show.
If all you need is basic routing, you are good to go! If, however, you need to redirect on undiscovered routes, or pass parameters to your routes, keep reading as we will now dive a little deeper!
React Router Redirect
What if you want to redirect to a specific Route if an unspecified route is used? For that, we have Redirect. Let’s start by creating a new NotFound component in our Routes folder.
NotFound.tsx
export const NotFound = () => { return <h1>Not Found!</h1>; };
Now, in App.tsx, import your new NotFound component and add Redirect to your imports from ‘react-router-dom’.
import { BrowserRouter as Router, Route, Link, Switch, Redirect } from 'react-router-dom'; import { NotFound } from './Routes/NotFound';
Now, add an exact Route to NotFound, and a Redirect element that points to your NotFound element at the end of the Switch:
<Route path="/notfound" exact component={NotFound} /> <Redirect to="/notfound" />
Let’s also add a new link to a bad route to make testing this redirect easier.
<li><Link to="/badlink">Bad Link</Link></li>
With these changes, App.tsx should now look like this:
import { BrowserRouter as Router, Route, Switch, Link, Redirect } from 'react-router-dom'; import { Home } from './Routes/Home'; import { About } from './Routes/About'; import { NotFound } from './Routes/NotFound'; const App = () => { return ( <> <Router> <h1>Learning React Router!</h1> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/badlink">Bad Link</Link></li> </ul> <Switch> <Route path="/" exact component={Home}/> <Route path="/about" exact component={About} /> <Route path="/notfound" exact component={NotFound} /> <Redirect to="/notfound" /> </Switch> </Router> </> ); }; export default App;
When you run this and click on the the new Bad link, notice how the url changes to /notfound, and ‘Not Found!’ is displayed in the browser! This means when a user tries to navigate to an undefined route, they will be redirected to this NotFound route.
<!-- Example of conditional Redirect --> <Route path="/" exact render={ () => user.isAuthenticated ? <Redirect to="/dashboard" /> : <Redirect to="/login" /> } />
Passing Params
The last thing this article will touch on is routing with parameters.
Route Params
Let’s add one more Route to our application. Let’s create Hello.tsx in the Routes folder:
export const Hello = () => { return <h1>Hello!</h1>; };
We need to add an import for this Route in App.tsx:
import { Hello } from './Routes/Hello';
Add a ‘hello’ Route above our NotFound Route in App.tsx:
<Route path="/hello" exact component={Hello} />
Now, add some new links in App.tsx. Let’s use some names as an added parameter to the hello Routes:
<li><Link to="/hello">Hello</Link></li> <li><Link to="/hello/nick">Nick</Link></li> <li><Link to="/hello/kristina">Kristina</Link></li> <li><Link to="/hello/frank">Frank</Link></li>
Notice how currently, when you try to go to any of the ‘hello’ links with name parameters, you are redirected to the NotFound route. Let’s fix this by adding a new route that specifies a ‘name’ route param to pass to the hello Route:
<Route path="/hello/:name" exact component={Hello} />
Now, when you click on any of the ‘hello’ links with names, everything looks the same as when you click on the normal ‘hello’ link. To see the difference, you can use the useRouteMatch function from ‘react-router-dom’. This function gives you information about the current route. One of the properties this function has available is the params of the route.
Update Hello.tsx to look like this to see the params:
import { useRouteMatch } from "react-router-dom"; export const Hello = () => { const routeParams = useRouteMatch().params; return ( <> <h1>Hello!</h1> <p>Route Params: <code>{JSON.stringify(routeParams)}</code></p> </> ); };
If you click on the regular ‘hello’ link, you should see this:
Route Params:
Click on the ‘Nick’ link and you should see this:
Route Params : {"name":"Nick"}
That’s the name we passed, called ‘name’, no less! You can use the json for anything you need now!
Query Params
The last thing we will go over is passing query params. First, add some new links in App.tsx for your ‘name’ Routes and add each person’s hobby as a query param (so we can show the route params and query params working in tandem):
<li><Link to="/hello/nick?hobby=programming">Nick w/Hobby</Link></li> <li><Link to="/hello/kristina?hobby=photography">Kristina w/Hobby</Link></li> <li><Link to="/hello/frank?hobby=reading">Frank w/Hobby</Link></li>
To access the query params, we also need to import useLocation (which is similar to useRouteMatch, but for query params) from ‘react-router-dom’ and add a couple lines to Hello.tsx:
import { useRouteMatch, useLocation } from "react-router-dom"; export const Hello = () => { const routeParams = useRouteMatch().params; const queryParams = useLocation().search; return ( <> <h1>Hello!</h1> <p>Route Params: <code>{JSON.stringify(routeParams)}</code></p> <p>Query Params: <code>{queryParams}</code></p> </> ); };
You can see that your query params are now showing, but it probably looks a little strange.
Learning how to decipher this information is well beyond the scope of this article, so we will use a helper here. In most modern browsers you can use the URLSearchParams browser api. Add this line above your return statement to make this more readable:
const queryParams = new URLSearchParams(useLocation().search);
Now, we can choose which query param we want the value for. Change the ‘Query Params’ line to this:
<p>Query Params: <code>{queryParams.get('hobby')}</code></p>
The final version of Hello.tsx should look like this:
import { useRouteMatch, useLocation } from "react-router-dom"; export const Hello = () => { const routeParams = useRouteMatch().params; const queryParams = new URLSearchParams(useLocation().search); return ( <> <h1>Hello!</h1> <p>Route Params: <code>{JSON.stringify(routeParams)}</code></p> <p>Query Params: <code>{queryParams.get('hobby')}</code></p> </> ); };
Just for a check, App.tsx should look like this now:
import { BrowserRouter as Router, Route, Switch, Link, Redirect } from 'react-router-dom'; import { Home } from './Routes/Home'; import { About } from './Routes/About'; import { NotFound } from './Routes/NotFound'; import { Hello } from './Routes/Hello'; const App = () => { return ( <Router> <ul> <li><Link to="/">Home</Link></li> <li><Link to="/about">About</Link></li> <li><Link to="/badlink">Bad Link</Link></li> <li><Link to="/hello">Hello</Link></li> <li><Link to="/hello/nick">Nick</Link></li> <li><Link to="/hello/kristina">Kristina</Link></li> <li><Link to="/hello/frank">Frank</Link></li> <li><Link to="/hello/nick?hobby=programming">Nick w/Hobby</Link></li> <li><Link to="/hello/kristina?hobby=photography">Kristina w/Hobby</Link></li> <li><Link to="/hello/frank?hobby=reading">Frank w/Hobby</Link></li> </ul> <Switch> <Route path="/" exact component={Home}/> <Route path="/about" exact component={About} /> <Route path="/hello" exact component={Hello} /> <Route path="/hello/:name" exact component={Hello} /> <Route path="/notfound" exact component={NotFound} /> <Redirect to="/notfound" /> </Switch> </Router> ); }; export default App;
You are now able to pass query params and parse them as well! That’s the basics (and a little more)!
Recap
React Router is a very powerful (and often necessary) tool. With what we’ve covered here, you should now have a basic understanding of how it works!
Here’s what we’ve covered:
Creating a React application and installing necessary dependencies
Creating simple components (so we have routes to visit)
Router (BrowserRouter and HashRouter)
Routes
Links
Switches
Redirects
Routing with Route Params
Routing with Query Params
Hopefully this article has been helpful, and you feel much more comfortable using React Router! Thanks for reading!