Protected Routes in React using React Router v6

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

Protected Routes in React using React Router v6

What is React Router?

React Router is the standard routing library for React. From the docs:

🗒️
React Router is a fully-featured client and server-side routing library for React, a JavaScript library for building user interfaces. React Router runs anywhere React runs; on the web, on the server with node.js, and on React Native.


What is Protected Routes?

Protected Routes known also as Private Routes and also Authorized Routes in React Router require a user being authorized to visit a route. So if a user is not authorized for a specific page, they cannot access it.

The most usual example is authentication in a React application where a user can only access the protected web pages when they are authorized (which means in this situation being authenticated).

Authorization goes beyond authentication though. For example, a user can also have roles and permissions which give a user access to specific areas of the application.

About this Tutorial

This is a React Router tutorial which teaches you how to use Private Routes with React Router 6.

Using a bundler

Feel free to use your bundler of choice like:

🐧 Vite

Using YARN 👷‍♂️

This will clone the JavaScript react template.

$ yarn create vite krython-app --template react

This will clone the TypeScript react template.

$ yarn create vite krython-app --template react-ts

Using NPM 👷‍♀️

This will clone the JavaScript react template.npm 6.x

$ npm create vite@latest krython-app --template react

npm 7+, extra double-dash is needed:

$ npm create vite@latest krython-app -- --template react

🐧 Create React App

If you've previously installed create-react-app globally via npm install -g create-react-app, we recommend you uninstall the package using npm uninstall -g create-react-app or yarn global remove create-react-app to ensure that npx always uses the latest version. 

Using YARN 👷‍♂️

This will clone the JavaScript react template.

$ yarn create react-app krython-app

This will clone the TypeScript react template.

$ yarn create react-app krython-app --template typescript

Using NPM 👷‍♀️

This will clone the JavaScript react template.

$ npx create-react-app krython-app

This will clone the TypeScript react template.

$ npx create-react-app krython-app --template typescript

Verify that your setup works

Navigate to your app folder which is krython-app in our case then start it

Using YARN 👷‍♂️

🐧 Vite

navigate to our app

$ cd krython-app

install node modules

$ yarn

start react app

$ yarn dev

🐧 Create React App

$ cd krython-app
$ yarn start

Using NPM 👷‍♀️

🐧 Vite

navigate to our app

$ cd krython-app

install node modules

$ npm i

start react app

$ npm run dev

🐧 Create React App

$ cd krython-app
$ npm run start

Using Vite navigate to http://localhost:3000/ then you should see:

react router vite app

Using Create React Route navigate to http://localhost:3000/ then you should see:

React Router Create React App

Add React Router to existing project

Navigate to your app folder then install React Router

Using YARN 👷‍♂️

yarn add react-router-dom@6

Using NPM 👷‍♀️

npm install react-router-dom@6


Let's Begin

Now we should have everything needed to start using React Router.

Create React App && Vite bundlers have the same concepts for adding React Router. So we will stick with Vite for the rest of this tutorial.

🌟 Connect the URL

First things first, we want to connect your app to the browser's URL:  import BrowserRouter and render it around your whole app.

In our case we will import it and use it inside main.jsx file.

krython app - main.jsx

After adding, it should look like this :

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")
);

Remove your App.jsx file we no longer need it.

🌟 Create App Router

Now we need to create an App Router known as App Container and App Navigator. we will put inside it our layout and our pages and it will teach React Router how to render our app at different URLs.

💁
Don't take the styling too seriously in this tutorial, we're just using inline styles for convenience, style your apps however you want.

Create a AppRouter.jsx inside our src folder and put:

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;

🌟 Create Our Layout

Layout is the wrapper component (THE PARENT COMPONENT) of our pages, we will add the navigations links inside it so we don't have to add it inside all of our pages.

import React from "react";

import { Link, Outlet } from "react-router-dom";

const Layout = () => {
  return (
    <>
      <h1>Protected Routes in React using React Router v6</h1>

      <ul>
        <li>
          <Link to="/">Public Page</Link>
        </li>
        <li>
          <Link to="/private">Private Page</Link>
        </li>
      </ul>

      <div>
        <Outlet />
      </div>
    </>
  );
};

export default Layout;
💁
Outlet - A component that renders our pages (RENDER OUR INNER PAGES).

🌟 Create Our Pages

Let's create our pages, as you saw before inside AppRouter.jsx we have three different pages. PublicPage page and PrivatePage page and LoginPage, they located inside pages folder.

React Router v6 Structure

Create Public.jsx inside pages folder.

import React from "react";

const PublicPage = () => {
  return <p>Public Page. I'm located at /.</p>;
};

export default PublicPage;

Create Private.jsx inside pages folder.

import React from "react";

const PrivatePage = () => {
  return <p>Private Page. I'm located at /private.</p>;
};

export default PrivatePage;

🌟 Create Auth Provider

Create Auth Provider and wrap it around our app, so we can keep track our authentication status.

Now create AuthProvider.jsx

import React from "react";

const AuthContext = React.createContext(null);

const AuthProvider = ({ children }) => {
  const [isAuthenticated, seIsAuthenticated] = React.useState(false);

  const login = (email, password, callback) => {
    // Do something to login

    seIsAuthenticated(true);

    // Simulate a delay
    setTimeout(() => {
      callback();
    }, 500);
  };

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

const useAuth = () => {
  const context = React.useContext(AuthContext);

  if (context === undefined) {
    throw new Error("useAuth must be used within a AuthProvider");
  }

  return context;
};

export { AuthProvider, useAuth };

Wrap our app with Auth Provider. Update Layout.jsx

import React from "react";

import { Link, Outlet } from "react-router-dom";

// Auth Provider
import { AuthProvider } from "./AuthProvider";

const Layout = () => {
  return (
    <AuthProvider>
      <h1>Protected Routes in React using React Router v6</h1>

      <ul>
        <li>
          <Link to="/">Public Page</Link>
        </li>
        <li>
          <Link to="/private">Private Page</Link>
        </li>
      </ul>

      <div>
        <Outlet />
      </div>
    </AuthProvider>
  );
};

export default Layout;

🌟 Create Require Auth Component

This component will redirect user to Login page if user not authenticated, else it will render the Private page.

Update AppRouter.jsx

import React from "react";

import { Routes, Route, Navigate } 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 Provider
import { useAuth } from "./AuthProvider";

const RequireAuth = ({ children }) => {
  const { isAuthenticated } = useAuth();

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

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;

🌟 Create Our Login Page

Finally, let's create our login page:

import React from "react";

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

// Auth Provider
import { useAuth } from "../AuthProvider";

const LoginPage = () => {
  const navigate = useNavigate();
  const { login } = useAuth();

  const handleSubmit = (event) => {
    event.preventDefault();

    const formData = new FormData(event.currentTarget);
    const { email, password } = {
      email: formData.get("email"),
      password: formData.get("password"),
    };

    login(email, password, () => {
      // Redirect after login
      navigate("/private", { replace: true });
    });
  };

  return (
    <div>
      <p>You must log in to view the private page</p>

      <form onSubmit={handleSubmit}>
        <label>
          Email: <input name="email" type="text" />
        </label>

        <label>
          Password: <input name="password" type="password" />
        </label>

        <button type="submit">Login</button>
      </form>
    </div>
  );
};

export default LoginPage;


Download The Full Code

GitHub - krython-web/react-protected-routes
Contribute to krython-web/react-protected-routes development by creating an account on GitHub.

Enjoying our content? Your support keeps us going! 🚀

Consider buying us a coffee to help fuel our creativity.