React Router Dom - Routes, React Router Hooks, Navigation and Protected Routes

Syed Aquib AliSyed Aquib Ali
7 min read

react-router-dom

React Router is a powerful library for handling routing in React applications. It provides a declarative way to define routes and manage navigation. Here’s a detailed guide:

Setting Up React Router

npm install react-router-dom

Routes & Route

Routes and Route components are used to define the routing logic in your application.

import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import Home from './Home';
import About from './About';
import Contact from './Contact';

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

export default App;

Nested Routes

Nested routes allow you to define routes within other routes, which is useful for hierarchical navigation.

import React from 'react';
import { BrowserRouter as Router, Routes, Route, Outlet } from 'react-router-dom';

// These can be a saperate components.
function Dashboard() {
  return (
    <div>
      <h2>Dashboard</h2>
      <Outlet /> {/* Render nested routes here */}
    </div>
  );
}

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

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

// Inside of our App component.
function App() {
  return (
    <Router>
      <Routes>
        <Route path="dashboard" element={<Dashboard />}>
          <Route path="profile" element={<Profile />} />
          <Route path="settings" element={<Settings />} />
        </Route>
      </Routes>
    </Router>
  );
}

export default App;

React Router Hooks

useLocation

import React from 'react';
import { useLocation } from 'react-router-dom';

function ShowLocation() {
  const location = useLocation();

  return (
    <div>
      <h3>Current Location</h3>
      <Link to={`${location.pathname}/newpath`}>New path</Link>
    </div>
  );
}

export default ShowLocation;

useParams

The useParams hook returns an object of key-value pairs of the dynamic params from the current URL that were matched by the Route.

import React from 'react';
import { useParams } from 'react-router-dom';

function UserProfile() {
  const { userId } = useParams();

  return (
    <div>
      <h3>User ID: {userId}</h3>
    </div>
  );
}

export default UserProfile;

Outlet

The Outlet component is used to render the child routes in a nested route structure. It serves as a placeholder for where the nested content should be rendered.

The Link component is used for navigation. It renders an anchor (<a>) element that links to a different route.

import React from 'react';
import { Link } from 'react-router-dom';

function Navigation() {
  return (
    <nav>
      <ul>
        <li>
          <Link to="/">Home</Link>
        </li>
        <li>
          <Link to="/about">About</Link>
        </li>
        <li>
          <Link to="/contact">Contact</Link>
        </li>
      </ul>
    </nav>
  );
}

export default Navigation;

NavLink is similar to Link but provides additional styling attributes to the link when it matches the current URL.

import React from 'react';
import { NavLink } from 'react-router-dom';

function Navigation() {
  return (
    <nav>
      <ul>
        <li>
          <NavLink to="/" end activeClassName="active">Home</NavLink>
        </li>
        <li>
          <NavLink to="/about" activeClassName="active">About</NavLink>
        </li>
        <li>
          <NavLink to="/contact" activeClassName="active">Contact</NavLink>
        </li>
      </ul>
    </nav>
  );
}

export default Navigation;

Redirects

The Navigate component is used to programmatically navigate to a different route, similar to redirect.

import React from 'react';
import { Routes, Route, Navigate } from 'react-router-dom';

function App() {
  return (
    <Routes>
      <Route path="/" element={<Home />} />
      <Route path="old-path" element={<Navigate to="/new-path" />} />
      <Route path="new-path" element={<NewComponent />} />
    </Routes>
  );
}

export default App;

Programmatic Navigation

The useNavigate hook allows for programmatic navigation.

import React from 'react';
import { useNavigate } from 'react-router-dom';

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

  const goToAbout = () => {
    navigate('/about');
  };

  return (
    <div>
      <button onClick={goToAbout}>Go to About</button>
    </div>
  );
}

export default Home;

React Router is a powerful library for managing navigation and routing in React applications.

  • Basic Routing: Using Route and Routes to define paths.

  • Nested Routes: Organizing hierarchical routes using Outlet.

  • Hooks: Utilizing useLocation, useParams, and useNavigate for accessing route data and programmatic navigation.

  • Linking: Using Link and NavLink for navigation.

  • Redirection: Using Navigate for redirects.

These features make React Router a flexible and efficient tool for handling complex routing scenarios in React applications.

Navigation

The useNavigate hook in React Router allows for programmatic navigation within a React application. This is useful for scenarios where you need to navigate to a different route as a result of an event, such as a button click, form submission, or after an API call.

import React from 'react';
import { BrowserRouter as Router, Routes, Route, useNavigate } from 'react-router-dom';

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

  const goToAbout = () => {
    navigate('/about');
  };

  return (
    <div>
      <h1>Home Page</h1>
      <button onClick={goToAbout}>Go to About</button>
    </div>
  );
}

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

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="about" element={<About />} />
      </Routes>
    </Router>
  );
}

export default App;

Passing State with Navigation

You can pass state to the new route using useNavigate. This state can be accessed in the destination component.

// Home Component
function Home() {
  const navigate = useNavigate();

  const goToAbout = () => {
    navigate('/about', { state: { fromHome: true } });
  };

  return (
    <div>
      <h1>Home Page</h1>
      <button onClick={goToAbout}>Go to About</button>
    </div>
  );
}

// About Component
import { useLocation } from 'react-router-dom';

function About() {
  const location = useLocation();
  const fromHome = location.state?.fromHome;

  return (
    <div>
      <h1>About Page</h1>
      {fromHome && <p>Navigated from Home Page</p>}
    </div>
  );
}

Navigating Back and Forward

useNavigate can also be used to navigate back and forward in the history stack.

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

  return (
    <div>
      <button onClick={() => navigate(-1)}>Go Back</button>
      <button onClick={() => navigate(1)}>Go Forward</button>
    </div>
  );
}

Replace the Current Entry

To replace the current entry in the history stack, you can use the replace option. This is useful when you don't want the user to go back to the previous route.

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

  const goToAbout = () => {
    navigate('/about', { replace: true });
  };

  return (
    <div>
      <h1>Home Page</h1>
      <button onClick={goToAbout}>Go to About</button>
    </div>
  );
}

Navigation with Dynamic Parameters

You can navigate to a route with dynamic parameters using useNavigate.

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

  const goToUser = (userId) => {
    navigate(`/user/${userId}`);
  };

  return (
    <div>
      <h1>Home Page</h1>
      <button onClick={() => goToUser(1)}>Go to User 1</button>
      <button onClick={() => goToUser(2)}>Go to User 2</button>
    </div>
  );
}

function User() {
  const { userId } = useParams();

  return (
    <div>
      <h1>User Page</h1>
      <p>User ID: {userId}</p>
    </div>
  );
}

function App() {
  return (
    <Router>
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="user/:userId" element={<User />} />
      </Routes>
    </Router>
  );
}

export default App;
  • Basic Navigation: Use useNavigate to navigate to a new route programmatically.

  • Passing State: Pass state to the new route using the state option.

  • History Navigation: Use negative anAuthentication Contextd positive integers to navigate back and forward in the history stack.

  • Replace Option: Use replace: true to replace the current entry in the history stack.

  • Dynamic Parameters: Navigate to routes with dynamic parameters.

The useNavigate hook provides a flexible and powerful way to handle navigation in your React applications, making it easier to manage route changes based on user actions and application logic.

Protected Routes

  1. Authentication Context

import React, { createContext, useContext, useState } from 'react';

// Create a context for authentication
const AuthContext = createContext();

export function AuthProvider({ children }) {
  const [isAuthenticated, setIsAuthenticated] = useState(false);

  const login = () => setIsAuthenticated(true);
  const logout = () => setIsAuthenticated(false);

  return (
    <AuthContext.Provider value={{ isAuthenticated, login, logout }}>
      {children}
    </AuthContext.Provider>
  );
}

// Custom hook to use the Auth context
export const useAuth = () => useContext(AuthContext);
  1. Protected Route Component

import React from 'react';
import { Navigate, Outlet } from 'react-router-dom';
import { useAuth } from './AuthProvider';

function ProtectedRoute() {
  const { isAuthenticated } = useAuth();

  return isAuthenticated ? <Outlet /> : <Navigate to="/login" />;
}

export default ProtectedRoute;
  1. Defining Routes

import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import { AuthProvider } from './AuthProvider';
import Home from './Home';
import Login from './Login';
import Dashboard from './Dashboard';
import ProtectedRoute from './ProtectedRoute';

function App() {
  return (
    <AuthProvider>
      <Router>
        <Routes>
          <Route path="/" element={<Home />} />
          <Route path="/login" element={<Login />} />
          <Route path="/dashboard" element={<ProtectedRoute />}>
            <Route path="" element={<Dashboard />} />
          </Route>
        </Routes>
      </Router>
    </AuthProvider>
  );
}

export default App;
  • AuthProvider: Provides authentication state and functions (login, logout) to manage it.

  • useAuth Hook: Custom hook to access authentication state and functions.

  • ProtectedRoute Component: Checks authentication status and either renders the protected component (<Outlet />) or redirects to the login page using the Navigate component.

  • Routes Setup: Uses ProtectedRoute to protect the /dashboard route.

Using the Navigate component and custom authentication logic, you can effectively protect routes in a React application. This approach ensures that only authenticated users can access specific routes, enhancing the security and user experience of your application.

0
Subscribe to my newsletter

Read articles from Syed Aquib Ali directly inside your inbox. Subscribe to the newsletter, and don't miss out.

Written by

Syed Aquib Ali
Syed Aquib Ali

I am a MERN stack developer who has learnt everything yet trying to polish his skills 🥂, I love backend more than frontend.