+
azure
solid
bun
json
+
julia
cassandra
+
+
+
+
pascal
swift
+
sse
+
?
dart
+
r
::
+
jax
fiber
+
jwt
+
+
+
cobol
dynamo
pinecone
+
=
!==
jax
+
+
+
||
+
?
html
+
+
+
fastapi
couchdb
perl
+
ios
+
flask
π
f#
nim
+
+
+
!!
raspbian
+
+
gulp
+
+
+
+
tls
+
+
symfony
oauth
+
travis
vercel
+
solidity
_
+
+
+
+
+
next
+
Back to Blog
Protected Routes in React using React Router v6
React Web Development React Router

Protected Routes in React using React Router v6

Published Nov 13, 2023

Learn to create Protected Routes known also as Private Routes in React using React Router v6

7 min read
0 views
Table of Contents

What is React Router?

React Router is the standard routing library for React. As the documentation states:

React Router is a fully-featured client and server-side routing library for React, a JavaScript library for building user interfaces.

It enables navigation between different components in a React application, allows changing the browser URL, and keeps the UI in sync with the URL.

What are Protected Routes?

Protected Routes (also known as Private Routes) are routes that require user authorization to access. Typically, this means a user must be authenticated to view certain pages.

Common use cases include:

  • User dashboards
  • Admin panels
  • User profile pages
  • Any content that requires login

Tutorial Setup

Before we begin, make sure you have a React application set up. You can use either Vite or Create React App.

Using a Bundler (Vite or Create React App)

Install React Router:

Using Yarn

yarn add react-router-dom@6

Using NPM

npm install react-router-dom@6

Implementation Steps

1. Connect URL in main.jsx

First, wrap your app with BrowserRouter to enable routing:

import React from "react";
import ReactDOM from "react-dom";
import { BrowserRouter } from "react-router-dom";

import "./index.css";
import AppRouter from "./AppRouter";

ReactDOM.render(
  <React.StrictMode>
    <BrowserRouter>
      <AppRouter />
    </BrowserRouter>
  </React.StrictMode>,
  document.getElementById("root")
);

2. Create AppRouter.jsx

Set up your routes structure:

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

// Layout
import Layout from "./Layout";

// Pages
import PublicPage from "./pages/Public";
import LoginPage from "./pages/Login";
import PrivatePage from "./pages/Private";

const AppRouter = () => {
  return (
    <Routes>
      <Route element={<Layout />}>
        <Route path="/" element={<PublicPage />} />
        <Route path="/login" element={<LoginPage />} />
        <Route path="/private" element={<PrivatePage />} />
      </Route>
    </Routes>
  );
};

export default AppRouter;

3. Create Layout Component

Create a layout with navigation and authentication provider:

import React from "react";
import { Link, Outlet } from "react-router-dom";
import { AuthProvider } from "./AuthProvider";

const Layout = () => {
  return (
    <AuthProvider>
      <header>
        <nav>
          <Link to="/">Public Page</Link>
          <Link to="/private">Private Page</Link>
        </nav>
      </header>
      <main>
        <Outlet />
      </main>
    </AuthProvider>
  );
};

export default Layout;

4. Create Authentication Provider

Implement authentication context for managing user state:

import React, { createContext, useState, useContext } from "react";
import { useNavigate, useLocation } from "react-router-dom";

const AuthContext = createContext(null);

export const AuthProvider = ({ children }) => {
  const navigate = useNavigate();
  const location = useLocation();
  const [user, setUser] = useState(null);

  const handleLogin = async () => {
    const user = { email: "[email protected]" };
    setUser(user);
    const origin = location.state?.from?.pathname || "/private";
    navigate(origin);
  };

  const handleLogout = () => {
    setUser(null);
  };

  const value = {
    user,
    onLogin: handleLogin,
    onLogout: handleLogout,
  };

  return (
    <AuthContext.Provider value={value}>
      {children}
    </AuthContext.Provider>
  );
};

export const useAuth = () => {
  return useContext(AuthContext);
};

5. Create Page Components

Public Page

import React from "react";

const PublicPage = () => {
  return (
    <div>
      <h1>Public Page</h1>
      <p>This page is accessible to everyone.</p>
    </div>
  );
};

export default PublicPage;

Login Page

import React from "react";
import { useAuth } from "../AuthProvider";

const LoginPage = () => {
  const { onLogin } = useAuth();
  
  return (
    <div>
      <h1>Login Page</h1>
      <button onClick={onLogin}>Sign In</button>
    </div>
  );
};

export default LoginPage;

Private Page

import React from "react";
import { useAuth } from "../AuthProvider";

const PrivatePage = () => {
  const { user, onLogout } = useAuth();
  
  return (
    <div>
      <h1>Private Page</h1>
      <p>Welcome, {user?.email}!</p>
      <button onClick={onLogout}>Sign Out</button>
    </div>
  );
};

export default PrivatePage;

6. Create RequireAuth Component

This is the key component that protects routes:

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

const RequireAuth = ({ children }) => {
  const { user } = useAuth();
  const location = useLocation();

  if (!user) {
    return <Navigate to="/login" state={{ from: location }} replace />;
  }

  return children;
};

export default RequireAuth;

7. Update AppRouter with Protected Routes

Wrap private routes with RequireAuth:

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

// Layout
import Layout from "./Layout";

// Pages
import PublicPage from "./pages/Public";
import LoginPage from "./pages/Login";
import PrivatePage from "./pages/Private";

// Auth
import RequireAuth from "./RequireAuth";

const AppRouter = () => {
  return (
    <Routes>
      <Route element={<Layout />}>
        <Route path="/" element={<PublicPage />} />
        <Route path="/login" element={<LoginPage />} />
        <Route
          path="/private"
          element={
            <RequireAuth>
              <PrivatePage />
            </RequireAuth>
          }
        />
      </Route>
    </Routes>
  );
};

export default AppRouter;

How It Works

  1. AuthProvider: Manages authentication state and provides login/logout functions
  2. RequireAuth: Checks if user is authenticated before rendering protected content
  3. Navigation: Redirects unauthenticated users to login page
  4. State Preservation: Remembers where users were trying to go before login

Best Practices

  1. Centralize Authentication: Use Context API or state management libraries
  2. Secure API Calls: Always validate authentication on the backend
  3. Handle Loading States: Show loading indicators during auth checks
  4. Persist Auth State: Use localStorage or cookies for session persistence
  5. Clear Navigation: Provide clear login/logout options

Conclusion

Protected routes are essential for building secure React applications. React Router v6 makes implementation straightforward with its hooks and component-based approach. Remember that client-side protection is just the first layer - always validate authentication on your backend for true security.

This pattern can be extended to handle different user roles, permissions, and more complex authentication flows. The key is understanding the core concepts and adapting them to your specific needs.