A Step-by-Step Guide to Implementing React Router for Dynamic Single-Page Applications
Tags: javascript, react, webdev, programming
Published: 2023-09-27 16:57:24

<p data-pm-slice="1 3 []">Single Page Applications (SPAs) are web apps that dynamically update a single page without requiring full page reloads. This results in a smooth user experience similar to a native mobile app.</p>
React is a popular JavaScript library for building user interfaces and is commonly used for building SPAs. To handle navigation in a React SPA, we need a routing solution.

React Router is the most widely used routing library for React. It allows us to build a single-page app with multiple views and navigational components while keeping the UI in sync with the URL.

In this comprehensive beginner's guide, we will cover the following:
<ul>
 	<li>What is client-side routing, and how does React Router implement it</li>
 	<li>Installing and setting up React Router in a React app</li>
 	<li>Creating routes to define app navigation</li>
 	<li>Matching route parameters to render dynamic components</li>
 	<li>Implementing nested routing to organize related screens</li>
 	<li>Building navigation with React Router links</li>
 	<li>Navigating programmatically with React Router hooks</li>
 	<li>Redirecting and preventing navigation</li>
 	<li>Animating transitions between routes</li>
 	<li>Lazy loading routes to optimize performance</li>
 	<li>Protecting routes with authentication</li>
 	<li>Handling 404 pages and route not found errors</li>
 	<li>React Router configuration and customization</li>
</ul>
Follow along as we explore each topic in detail and build a dynamic React app with routing from scratch!
<h4><strong>What is Client-Side Routing?</strong></h4>
Traditionally, routing was handled on the server-side. This required making a round-trip to the server anytime you needed to navigate to a new page.

With <strong>client-side routing</strong>, navigation happens on the front-end without requiring a server request. The router manipulates the browser history and updates the UI in response to URL changes.

Some benefits of client-side routing:
<ul>
 	<li>Faster page transitions without reloading</li>
 	<li>A smoother user experience similar to a mobile app</li>
 	<li>Easier to implement features like transitions and animations</li>
</ul>
React Router embraces client-side routing and gives us routing capabilities directly in our React app.
<h4><strong>How React Router Works</strong></h4>
React Router implements client-side routing by keeping your UI <strong>in sync with the browser URL</strong>.

It uses the browser history API to intercept URL changes and navigation events. When the URL changes, React Router will match a route, render the appropriate screen, and pass down router parameters to that screen.

The components of React Router include:
<ul>
 	<li>&lt;BrowserRouter&gt; - The main router component</li>
 	<li>&lt;Route&gt; - Defines our routes and renders components</li>
 	<li>&lt;Link&gt;/&lt;NavLink&gt; - Link components for navigation</li>
 	<li>useParams, useNavigate, etc - Router hooks</li>
</ul>
By wiring up &lt;Route&gt;'s and &lt;Link&gt;'s, we can build a complete client-side routing solution for our React Single Page App.
<h4><strong>Set Up a React App</strong></h4>
First, we need a React app to work with. Make sure you have Node.js installed, then run:
```javascript
npx create-react-app my-app
cd my-app
```
This will scaffold a new React app using Create React App. Alternatively, you can use your preferred method to set up React.

When you create a new React app, it sets up a development server using webpack that bundles your app and automatically refreshes when you make changes.

The start script in your package.json runs this development server:
```javascript
// package.json
"scripts": {
  "start": "react-scripts start" 
}
```
So after installing dependencies with npm install, you can run:

<code>npm start</code>

This will start the React dev server and open your app in the browser at <a href="http://localhost:3000" target="_blank" rel="noopener noreferrer nofollow">http://localhost:3000</a>.

Some key points on npm start:
<ul>
 	<li>It compiles your React code and bundles it ready for the browser</li>
 	<li>It watches for changes and automatically reloads the browser</li>
 	<li>It sets up Hot Module Replacement for instant refresh without losing state</li>
 	<li>It configures Webpack and Babel behind the scenes</li>
 	<li>The dev server supports hot reloading of CSS/modules without refreshing</li>
</ul>
npm start handles all the complex config required to run and develop a React app - it builds the app, runs the dev server, and refreshes as you code!

Once you're ready to deploy, you would then use npm run build to build the optimized production bundle output. But npm start is all you need to build your React app for development.
<h4><strong>Installing React Router</strong></h4>
To install React Router in your project:

<code>npm install react-router-dom</code>

This will install the react-router-dom package containing the routing components we need.

Alternatively, use Yarn:

<code>yarn add react-router-dom</code>

Now, let's set up a React Router in a React app.
<h4><strong>Set Up React Router in the App</strong></h4>
In your App.js, import the core routing components:

<code>import { BrowserRouter, Routes, Route } from 'react-router-dom';</code>
<ul>
 	<li>&lt;BrowserRouter&gt; - The router wrapper component</li>
 	<li>&lt;Routes&gt; - Holds all the &lt;Route&gt;'s</li>
 	<li>&lt;Route&gt; - Defines the routes</li>
</ul>
Wrap the app in &lt;BrowserRouter&gt;:
```javascript
function App() {
  return (
    &lt;BrowserRouter&gt;
      &lt;h1&gt;React Router App&lt;/h1&gt;
    &lt;/BrowserRouter&gt;
  );
}
```
This sets up the basic React Router structure.
<h4><strong>Defining Routes</strong></h4>
The &lt;Route&gt; component defines our routes and renders UI when a location matches.

It takes two key props:
<ul>
 	<li>path - The route path URL</li>
 	<li>element - The JSX element to render</li>
</ul>
<code>&lt;Route path="/about" element={&lt;About /&gt;} /&gt;</code>

This will match the /about path and render the &lt;About&gt; component.

We can define multiple routes:
<pre><code class="language-javascript">&lt;Routes&gt;
  &lt;Route path="/" element={&lt;Home /&gt;} /&gt;
  &lt;Route path="/about" element={&lt;About /&gt;} /&gt;
  &lt;Route path="/contact" element={&lt;Contact /&gt;} /&gt;
&lt;/Routes&gt;</code></pre>
Now, different components will render on each route!
<h4><strong>Exact Path Matching</strong></h4>
By default, &lt;Route&gt; will match any path that starts with its path.

To make it match exactly, use the exact prop:

<code>&lt;Route exact path="/" element={&lt;Home /&gt;} /&gt;</code>

This will only render &lt;Home&gt; on the exact / URL.
<h4><strong>Linking Between Routes</strong></h4>
To link between routes, use the &lt;Link&gt; component:

<code>&lt;Link to="/about"&gt;About&lt;/Link&gt;</code>

This will render an anchor tag that navigates to /about when clicked.

We should use &lt;Link&gt; instead of anchor tags to enable client-side routing.
<h4><strong>Matching Route Parameters</strong></h4>
To match a dynamic segment in the URL, we can use the: colon syntax:

<code>&lt;Route path="/users/:id" element={&lt;User /&gt;} /&gt;</code>

This will match /users/1, /users/2, etc.

The :id parameter can be accessed inside &lt;User&gt; with the useParams hook:

<code>let { id } = useParams();</code>

We can now render user data dynamically based on the ID!
<h4><strong>Nested Routing</strong></h4>
We can render components as children of other routes to achieve <strong>nested routing</strong>:
```javascript
&lt;Route path="/users" element={&lt;Users /&gt;}&gt;
  &lt;Route path=":id" element={&lt;User /&gt;} /&gt;
&lt;/Route&gt;
```
This will match nested paths like:
<ul>
 	<li>/users - Renders parent &lt;Users&gt;</li>
 	<li>/users/1 - Renders child &lt;User&gt;</li>
</ul>
Great for related screens!
<h4><strong>Using Outlet for Nested Routes</strong></h4>
The parent route element can render the child route using the &lt;Outlet&gt; component:
```javascript
&lt;Route path="/users" element={&lt;Users /&gt;}&gt;
  {/* Users.js */}
  &lt;Outlet /&gt;
&lt;/Route&gt;
```
This will render the child route element in the &lt;Outlet&gt; position.
<h4><strong>Index Routes</strong></h4>
We can define an "index" child route that renders when no child path is defined:
```javascript
&lt;Route path="/users" element={&lt;Users /&gt;}&gt;
  &lt;Route 
    index
    element={&lt;FeaturedUsers /&gt;} 
  /&gt;
  &lt;Route 
    path=":id" 
    element={&lt;User /&gt;} 
  /&gt;
&lt;/Route&gt;
```
Now /users will render the &lt;FeaturedUsers&gt; index route.
<h4><strong>Linking with Active Styling</strong></h4>
The &lt;NavLink&gt; component adds styling when active:
```javascript
&lt;NavLink 
  to="/about"
  style={({ isActive }) =&gt; (isActive ? { color: 'red' } : undefined)}
&gt;
  About
&lt;/NavLink&gt;
```
This will turn red when on the /about page.
<h4><strong>Navigating Programmatically</strong></h4>
Sometimes, we want to navigate on some event like a form submission.

The useNavigate hook returns a navigate function to navigate programmatically:
```javascript
import { useNavigate } from 'react-router-dom';
function Login() {
  let navigate = useNavigate(); 
  function handleLogin() {

    // Login logic...
    navigate('/dashboard');
  }
  return (
    &lt;button onClick={handleLogin}&gt;Login&lt;/button&gt;
  );
}
```
Calling navigate('/dashboard') will navigate there.

The navigate function can also pass state:

<code>navigate('/dashboard', { user: </code><a href="http://res.data" target="_blank" rel="noopener noreferrer nofollow"><code>res.data</code></a><code>.user })</code>

And replace the current entry in history:

<code>navigate('/dashboard', { replace: true })</code>
<h4><strong>Using Params and State</strong></h4>
We can access route params, state, location etc. using hooks:
```javascript
import { useParams, useLocation } from 'react-router-dom'
function Dashboard() {
  // Get route params
  let { userId }  = useParams();

  // Get route state
  let { state } = useLocation();
  return (
    &lt;div&gt;
      &lt;h1&gt;Dashboard&lt;/h1&gt;
      &lt;p&gt;User ID: {userId}&lt;/p&gt;
      &lt;p&gt;State: {JSON.stringify(state)}&lt;/p&gt; 
    &lt;/div&gt;
  );
}
```
This provides values from the matched route.
<h4><strong>Redirecting</strong></h4>
The &lt;Navigate&gt; component can redirect to another route:
```javascript
&lt;Route path="/login" element={&lt;Login /&gt;} /&gt;
&lt;Route 
  path="/dashboard"
  element={
    loggedIn ? &lt;Dashboard /&gt; : &lt;Navigate to="/login" /&gt;
  }
/&gt;
```
If not logged in, this will redirect to /login.

We can also use the replace prop to replace the history entry.
<h4><strong>Handling 404 Pages</strong></h4>
To show a 404 page on unknown routes, use a * catch-all route:

<code>&lt;Route path="*" element={&lt;NotFound /&gt;} /&gt;</code>

This will match any unknown route. We can also redirect to 404:

<code>&lt;Route path="*" element={&lt;Navigate to="/" replace /&gt;} /&gt;</code>

Now, unknown routes redirect to the home page.
<h4><strong>Lazy Loading Routes</strong></h4>
We can optimize our app by lazy loading route components only when needed:
```javascript
const Users = lazy(() =&gt; import('./Users'));
function App() {
  return (
    &lt;Routes&gt;
      &lt;Route 
        path="/users"
        element={
          &lt;Suspense fallback={&lt;Spinner /&gt;}&gt;
            &lt;Users /&gt;
          &lt;/Suspense&gt;
        }
      /&gt;
    &lt;/Routes&gt;
  );
}
```
Users will now load dynamically with React.lazy().
<h4><strong>Animating Route Transitions</strong></h4>
To animate transitions between routes, we can use:
<ul>
 	<li>CSS transitions based on location</li>
 	<li>React transition libraries like Framer Motion</li>
 	<li>Wrap routes in animated components</li>
</ul>
This enables smooth animated transitions as users navigate!
<h4><strong>Scroll Restoration</strong></h4>
By default, the scroll position resets on navigation.

To restore scroll on backward/forward navigation:
```javascript
&lt;BrowserRouter&gt;
  &lt;ScrollRestoration /&gt;
&lt;/BrowserRouter&gt;
```
&lt;ScrollRestoration&gt; will maintain the scroll position.
<h4><strong>Router Configuration</strong></h4>
We can customize React Router with a &lt;Router&gt; component:
```javascript
&lt;Router
  basename="/app"
  history={createBrowserHistory()}
&gt;
&lt;/Router&gt;
```
Allows configuring:
<ul>
 	<li>basename - Base URL for all locations</li>
 	<li>history - Custom history implementation</li>
</ul>
And more!
<h4><strong>Conclusion</strong></h4>
In this guide, we've explored the core concepts and APIs that power routing in React with React Router, including:
<ul>
 	<li>Using &lt;Route&gt; and &lt;Link&gt; to define routes and navigate</li>
 	<li>Dynamic route matching with parameters and nested routes</li>
 	<li>Programmatic navigation with hooks like useNavigate</li>
 	<li>Redirecting, animating, lazy loading, and more</li>
</ul>
React Router provides a robust routing solution for React single-page apps right out of the box. With declarative routes, dynamic matching, and location tracking, you can build complex navigational experiences and keep your UI in sync with URLs.

There is still much more to cover with React Router, including protected routes, custom hooks, server-side rendering, and more.

I hope this guide provides a solid foundation for getting started with routing in your React apps!

If you want to become a programmer, then this <a class="attrlink" href="https://blog.learnhub.africa/2023/09/12/how-to-learn-programming-in-2023/" target="_blank" rel="noopener noreferrer nofollow">article</a> is your guide to becoming one. It explains everything from start to finish on how to build technical skills and what to do.

If you find this post exciting, find more exciting posts on <a class="attrlink" href="http://blog.learnhub.africa/" target="_blank" rel="noopener noreferrer nofollow">Learnhub Blog</a>; we write everything tech from <a class="attrlink" href="https://blog.learnhub.africa/category/cloud-computing/" target="_blank" rel="noopener noreferrer nofollow">Cloud computing</a> to <a class="attrlink" href="https://blog.learnhub.africa/category/frontend/" target="_blank" rel="noopener noreferrer nofollow">Frontend Dev</a>, <a class="attrlink" href="https://blog.learnhub.africa/category/security/" target="_blank" rel="noopener noreferrer nofollow">Cybersecurity</a>, <a class="attrlink" href="https://blog.learnhub.africa/category/data-science/" target="_blank" rel="noopener noreferrer nofollow">AI</a>, and <a class="attrlink" href="https://blog.learnhub.africa/category/blockchain/" target="_blank" rel="noopener noreferrer nofollow">Blockchain</a>.
<h4><strong>Resource</strong></h4>
<ul>
 	<li><a class="attrlink" href="https://blog.learnhub.africa/2023/01/18/how-to-send-an-email-in-node-js-using-nodemailer/" target="_blank" rel="noopener noreferrer nofollow">How to send an email in node.js using nodemailer</a></li>
 	<li><a class="attrlink" href="https://www.w3schools.com/js/js_arrays.asp" target="_blank" rel="noopener noreferrer nofollow">Javascript Array method</a></li>
 	<li><a class="attrlink" href="https://blog.learnhub.africa/2023/05/09/20-best-javascript-frameworks-for-2023/" target="_blank" rel="noopener noreferrer nofollow">20 Best React JavaScript Frameworks For 2023</a></li>
 	<li><a class="attrlink" href="https://www.codecademy.com/learn/learn-how-to-code?periods=year&amp;plan_id=proGoldAnnualV2&amp;utm_source=pepperjam&amp;utm_medium=affiliate&amp;utm_term=96525&amp;clickId=4438617034&amp;pj_creativeid=8-12462&amp;pj_publisherid=96525" target="_blank" rel="noopener noreferrer nofollow">Getting Started with Programming</a></li>
 	<li><a class="attrlink" href="https://blog.learnhub.africa/2023/06/08/4-javascript-email-frameworks-nodemailer-sendgrid-smtp-js-and-mailgun/" target="_blank" rel="noopener noreferrer nofollow">Javascript Email with Nodemailer</a></li>
</ul>