By Hemanta Sundaray on 2021-10-04
In a single page application created using React, we can implement client side routing using React Router, which will render a certain component based on the route in the URL.
We will understand the fundamentals of React Router by creating an application with basic navigation between 3 pages and in the process learn the usage of the 5 most common components:
Create a folder called react-router, open the folder in Visual Studio Code and create a React project using create-react-app.
PS C:\Users\Delhivery\Desktop\react-router> npx create-react-app .
The name of the package used to implement React Router in a web application is called react-router-dom. Let’s install it.
PS C:\Users\Delhivery\Desktop\react-router> npm i react-router-dom
The <BrowserRouter/> component is a router that uses the HTML5 history API (mainly the pushState method that allows changing the URL of the page without triggering a browser refresh).
The <BrowserRouter/> keeps the user interface in sync with the URL, which means that the URL will always reflect the current route being rendered.
For <BrowserRouter/> to work, we have to wrap our entire application (the <App/> component) with the <BrowserRouter/> component.
import React from "react"
import ReactDOM from "react-dom"
import "./index.css"
import App from "./App"
import { BrowserRouter as Router } from "react-router-dom"
ReactDOM.render(
<Router>
<App />
</Router>,
document.getElementById("root")
)
Next, in the src folder, create a folder called components and inside the components folder, create 4 components: Header.js, Home.js, About.js & Contact.js.
import React from "react"
const Home = () => {
return <p>Welcome to NASA.</p>
}
export default Home
import React from "react"
const About = () => {
return (
<p>
NASA is America's space agency pioneering the future in space exploration.
</p>
)
}
export default About
import React from "react"
const Contact = () => {
return <p>NASA Headquarters, Washington DC</p>
}
export default Contact
import React from "react"
const Header = () => {
return (
<nav>
<ul>
<li>
<a href="#">Home</a>
</li>
<li>
<a href="#">About</a>
</li>
<li>
<a href="#">Contact</a>
</li>
</ul>
</nav>
)
}
export default Header
We will import these 4 components inside the App component.
import React from "react"
import About from "./components/About"
import Contact from "./components/Contact"
import Home from "./components/Home"
import Header from "./components/Header"
const App = () => {
return (
<>
<Header />
<main>
<About />
<Contact />
<Home />
</main>
</>
)
}
export default App
Start the development server using npm start.
PS C:\Users\Delhivery\Desktop\react-router> npm start
Our application looks like the following:
Next, we will improve the styling. Copy and paste the following style rules in index.css.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-family: sans-serif;
font-size: 62.5%;
}
nav {
width: 40rem;
margin: 2rem;
margin-bottom: 3rem;
}
nav ul {
width: 100%;
display: flex;
justify-content: flex-start;
list-style: none;
}
nav li {
font-size: 1.4rem;
margin-right: 2rem;
}
nav a {
font-weight: 900;
text-decoration: none;
}
nav a:link,
a:visited {
color: darkgray;
}
nav a:hover {
color: black;
}
main {
margin: 2rem;
font-size: 2rem;
}
Now, our application looks better:
When we link to a new page using the anchor tag, for example, <a href="/about">About Us</a>, the link ends up performing a page navigation, which means the React application restarts. This is because the browser thinks we are navigating to a completely new page, so it makes a HTTP request to /about, and then the browser has to parse the HTML and the script etc.
With React Router however, we can have instant navigation by replacing the <a> link with the <Link /> component.
The <Link/> component takes a to prop. When the user clicks on the link, React Router will navigate to the path provided by the to prop.
The <Link/> element will render an <a> element, but with some event handlers that will instruct React Router to do an instant redirect instead of a page navigation. React Router performs an event.preventDefault() to prevent the page from navigating to a new page.
Note that the <link/> element is accessible by default. This is because it ends up rendering an anchor tag <a>. This allows users that make use of a screen reader to know that this is a link and they will be able to click on it as if it were a normal link.
<NavLink /> is a special version of the <Link/> component that adds styling elements to the rendered element when it matches the current URL.
Let’s import the <NavLink/> component inside the Header component and replace the <a> tags with the <NavLink/> component.
import React from "react"
import { NavLink } from "react-router-dom"
const Header = () => {
return (
<nav>
<ul>
<li>
<NavLink to="/">Home</NavLink>
</li>
<li>
<NavLink to="/about">About</NavLink>
</li>
<li>
<NavLink to="/contact">Contact</NavLink>
</li>
</ul>
</nav>
)
}
export default Header
The <Switch /> component will wrap several <Route /> components and will render only the first <Route /> that matches the current browser URL.
Note that <Route /> has to be a direct descendant of the <Switch />. This means that we shouldn’t have a <div> or other elements in between the <Switch> and the <Route />.
The <Route /> component will conditionally render a component when the path prop matches the current URL in the browser.
Update the App.js file as follows:
import React from "react"
import About from "./components/About"
import Contact from "./components/Contact"
import Home from "./components/Home"
import Header from "./components/Header"
import { Switch, Route } from "react-router-dom"
const App = () => {
return (
<>
<Header />
<main>
<Switch>
<Route path="/about" exact>
<About />
</Route>
<Route path="/contact" exact>
<Contact />
</Route>
<Route path="/" exact>
<Home />
</Route>
</Switch>
</main>
</>
)
}
export default App
The routing works as expected. We have one issue: we can’t tell which link is active. We can fix this issue by using the activeClassName prop in the <NavLink/> component.
Make the following changes in the Header component:
import React from "react"
import { NavLink } from "react-router-dom"
const Header = () => {
return (
<nav>
<ul>
<li>
<NavLink to="/" exact activeClassName="selected">
Home
</NavLink>
</li>
<li>
<NavLink to="/about" activeClassName="selected">
About
</NavLink>
</li>
<li>
<NavLink to="/contact" activeClassName="selected">
Contact
</NavLink>
</li>
</ul>
</nav>
)
}
export default Header
Notice that we have added the
exactprop in the<NavLink/>component wrapping theHomelink. Can you tell why?
Next, add the highlighted style rules in the index.css file.
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
html {
font-family: sans-serif;
font-size: 62.5%;
}
nav {
width: 40rem;
margin: 2rem;
margin-bottom: 3rem;
}
nav ul {
width: 100%;
display: flex;
justify-content: flex-start;
list-style: none;
}
nav li {
font-size: 1.4rem;
margin-right: 2rem;
}
nav a {
font-weight: 900;
text-decoration: none;
}
nav a:link,
a:visited {
color: darkgray;
}
nav a:hover {
color: black;
}
.selected {
border-bottom: 0.2rem solid darkgray;
}
main {
margin: 2rem;
font-size: 2rem;
}
Our app is ready and the routing is working as expected.