DEV Community

John Mark Harrell
John Mark Harrell

Posted on

Understanding Client-Side Routing in React

For novice developers with some experience learning Javascript, learning to build apps with React is a fun experience that opens a world of possibilities and access that might have otherwise taken months or years to learn otherwise. That is, until you get to client-side routing. While it's conceptually easy to grasp its function and importance, it can be truly puzzling to implement for first-timers and make you re-think the paradigms you may have been operating under up to this point.

So why do we need client-side routing when we build React apps? The answer lies in both functionality as well as significant performance improvements.

Why Do We Need Client-Side Routing?

In traditional web applications, when a user clicks a link or enters a URL, the browser makes a request to the server, which returns a new HTML page. This process involves a full-page reload, which can slow things down depending on network speed and how much data the server returns.

Client-side routing, on the other hand, allows you to change the URL and update the displayed content without reloading the entire page. Instead of fetching a new HTML document from the server, a single-page application (SPA) loads all the necessary resources upfront or fetches them dynamically and uses JavaScript to manage navigation.

In React, client-side routing is handled using the react-router-dom library, which provides declarative routing components for managing navigation and rendering different components based on the URL path.

This is where things get tricky. How do we use react-router-dom to set up client-side routing?

Setting Up Client-Side Routing in React

First, make sure you have react-router-dom installed in your project:

npm install react-router-dom
Enter fullscreen mode Exit fullscreen mode

Next, let’s look at a simple example of how to implement client-side routing. Imagine a basic React application with three pages: Home, About, and Contact. Here’s how to set up routing for these pages.

import React from "react";
import ReactDOM from "react-dom/client";
import { BrowserRouter as Router, Route, Routes, Link } from "react-router-dom";

// Define the components for each route
function Home() {
  return <h2>Home Page</h2>;
}

function About() {
  return <h2>About Page</h2>;
}

function Contact() {
  return <h2>Contact Page</h2>;
}

// Main App component
function App() {
  return (
    <Router>
      <nav>
        <ul>
          <li><Link to="/">Home</Link></li>
          <li><Link to="/about">About</Link></li>
          <li><Link to="/contact">Contact</Link></li>
        </ul>
      </nav>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/about" element={<About />} />
        <Route path="/contact" element={<Contact />} />
      </Routes>
    </Router>
  );
}

// Render the app
const root = ReactDOM.createRoot(document.getElementById("root"));
root.render(<App />);
Enter fullscreen mode Exit fullscreen mode

In this example:

  • We use BrowserRouter (aliased as Router) as the top-level wrapper to enable client-side routing.
  • The Link component replaces the standard <a> tag, ensuring that navigation is handled by React without causing a full-page reload.
  • The Routes component contains multiple Route components, each specifying a path and the component to render for that path.

When , only the content on the page changes—there’s no full-page refresh. The application remains responsive, giving a more fluid experience similar to that of native apps.

Nested Routes

In more complex applications, you’ll often need nested routes. Here’s an example:

function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
      <ul>
        <li><Link to="profile">Profile</Link></li>
        <li><Link to="settings">Settings</Link></li>
      </ul>

      <Routes>
        <Route path="profile" element={<Profile />} />
        <Route path="settings" element={<Settings />} />
      </Routes>
    </div>
  );
}

function Profile() {
  return <h3>Profile Page</h3>;
}

function Settings() {
  return <h3>Settings Page</h3>;
}

function App() {
  return (
    <Router>
      <nav>
        <Link to="/">Home</Link>
        <Link to="/dashboard">Dashboard</Link>
      </nav>

      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/dashboard/*" element={<Dashboard />} />
      </Routes>
    </Router>
  );
}
Enter fullscreen mode Exit fullscreen mode

In this setup:

  • The /dashboard/* route handles paths like /dashboard/profile and /dashboard/settings by using nested routes within the Dashboard component.
  • Notice the use of /* to match any sub-routes inside the Dashboard route.

Handling 404 Pages

You can also handle cases where a user navigates to a non-existent page by adding a "catch-all" route.

<Route path="*" element={<NotFound />} />
Enter fullscreen mode Exit fullscreen mode

This route matches any path not explicitly defined and renders a custom 404 page.

Programmatic Navigation

Sometimes, you’ll need to navigate programmatically rather than relying on user clicks. You can do this using the useNavigate hook:

import { useNavigate } from "react-router-dom";

function Login() {
  const navigate = useNavigate();

  const handleLogin = () => {
    // Perform login logic here
    navigate("/dashboard");
  };

  return <button onClick={handleLogin}>Log In</button>;
}
Enter fullscreen mode Exit fullscreen mode

Advantages of Client-Side Routing

  • Improved Performance: By avoiding full-page reloads, the application feels faster.
  • Smooth User Experience: Navigation transitions can be handled more fluidly.
  • SEO Considerations: While client-side routing is beneficial, it can complicate search engine optimization (SEO). Tools like Next.js provide hybrid solutions that combine client-side routing with server-side rendering for better SEO.

Conclusion

Client-side routing in React enables you to build highly interactive, single-page applications where the user experience remains fluid and responsive. Libraries like react-router-dom make it easy to implement and manage routing logic declaratively, supporting a variety of complex navigation needs. Understanding these fundamentals allows you to create seamless SPAs, improving both performance and user satisfaction.

Top comments (0)