Protected routes are routes that allow access to authorized users only. This means that users must first meet certain conditions before accessing that specific route. This is essential for securing certain routes or information. There are various ways you can use protected routes in React. One of them is using a higher-order component that wraps a protected route and checks if a user is authenticated before rendering a component.

Creating a React Application

Before creating the protected route, you'll need to create a React app.

Run the command below to use the create-react-app command tool to bootstrap the application.

        npx create-react-app protect-routes-react
    

The create-react-app command will create a folder named protect-routes-react containing all the necessary files and packages to get started.

Navigate to the folder and run npm start to start the application.

        cd protect-routes-react
npm start

Open your application folder with your preferred text editor and modify App.js so that it only contains the following code.

        function App() {  
  return <div></div>;
}
export default App;

You are now ready to set up the routes.

Setting Up the React Router

Run the npm command below in the terminal to install react-router-dom.

        npm install react-router-dom
    

In this application, you will create three main pages:

  • Home page (the landing page).
  • Profile page (protected, so only logged-in users have access).
  • About page (public so anyone can access it).

To start, create a component named Navbar.js. This component will contain the navigation links to the three pages.

In this file, import the Link component from react-router-dom and add the links.

        const { Link } = require("react-router-dom");
const Navbar = () => {
  return (
    <nav style={{ textAlign: "center", marginTop: "20px" }}>
      <Link to="/" style={{ padding: "10px" }}>
        Home
      </Link>
      <Link to="/profile" style={{ padding: "10px" }}>
        Profile
      </Link>
      <Link to="/about" style={{ padding: "10px" }}>
        About
      </Link>
    </nav>
  );
};
export default Navbar;

After creating the links, create the matching routes in App.js.

        import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import Navbar from "./Navbar";
import Home from "./Home";
import Profile from "./Profile";
import About from "./About";
function App() {
  return (
    <Router>
      <Navbar />
      <Routes>
        <Route path="/" element={<Home />} />
        <Route path="/profile" element={<Profile />} />
        <Route path="/about" element={<About />} />
      </Routes>
    </Router>
  );
}
export default App;

Next, create the components you have referenced in App.js.

In Home.js add the following code to create the home page.

        const Home = () => {
  return <h1>Home page</h1>;
};
export default Home;

In Profile.js add the following code to create the profile page.

        const Profile = () => {
  return <h1 style={{ textAlign: "center" }}>Profile Page</h1>;
};
export default Profile;

Finally, in About.js, add the following code to create the about page.

        const About = () => {
  return <h1 style={{ textAlign: "center" }}>About page</h1>;
};
export default About;

Once you've created the pages, you can now create the protected routes.

Creating Protected Routes in React

The /home and /about routes are public routes meaning anyone can access them, but only authenticated users can access the profile route. You, therefore, need a way to log in as a user.

Setting Up Fake Authentication

Since this is not an authentication tutorial, you will use React useState hook to simulate a login system.

In App.js, add the following.

        import { Routes, Route, BrowserRouter } from "react-router-dom";
import { useState } from "react";
<em>// Other import stamements</em>
function App() {
  const [isLoggedIn, setisLoggedIn] = useState(null);
  const logIn = () => {
    setisLoggedIn(true);
  };
  const logOut = () => {
    setisLoggedIn(false);
  };
  return (
    <BrowserRouter>
      <Navbar />
      {isLoggedIn ? (
        <button onClick={logOut}>Logout</button>
      ) : (
        <button onClick={logIn}>Login</button>
      )}
      <Routes>
      </Routes>
    </BrowserRouter>
  );
}
export default App;

Here, you are tracking the login status of the user using state. You have two buttons, the login, and the logout button. These buttons are rendered in turn depending on whether a user is logged in or not.

If the user is logged out, the login button is displayed. Clicking on it will trigger the login function which will update the isLoggedIn state to true and in turn the display from login to the logout button.

Protecting Private Components

To protect routes, the private components must also have access to the isLoggedIn value. You can create a new component that accepts the isLoggedIn state as a prop and the private component as a child.

For instance, if your new component is named Protected, you would render a private component by wrapping it like this:

        <Protected isLoggedIn={isLoggedIn}>
  <Private />
</Protected>

The Protected component will check whether isLoggedIn is true or false. If it's true, it will go ahead and return the Private component. If it's false, it will redirect the user to a page where they can log in.

Learn more about other ways you can use to render a component depending on conditions from this article on conditional rendering in React.

In your application, create a file named Protected.js and add the following code.

        import { Navigate } from "react-router-dom";
const Protected = ({ isLoggedIn, children }) => {
  if (!isLoggedIn) {
    return <Navigate to="/" replace />;
  }
  return children;
};
export default Protected;

In this component, the if statement is used to check whether the user is authenticated. If they are not, Navigate from react-router-dom redirects them to the home page. However, if the user is authenticated, the component renders the children.

To protect the Profile route, wrap it with the Protected route and pass in the isLoggedIn state as a prop.

        <Route
  path="/profile"
  element={
    <Protected isLoggedIn={isLoggedIn}>
      <Profile />
    </Protected>
  }
/>

The App component should now look like this.

        import { Routes, Route, BrowserRouter } from "react-router-dom";
import { useState } from "react";
import Navbar from "./Navbar";
import Protected from "./Protected";
import Home from "./Home";
import About from "./About";
import Profile from "./Profile";
function App() {
 const [isLoggedIn, setisLoggedIn] = useState(null);
 const logIn = () => {
   setisLoggedIn(true);
 };
 const logOut = () => {
   setisLoggedIn(false);
 };
 return (
   <BrowserRouter>
   <div>
     <Navbar />
     {isLoggedIn ? (
       <button onClick={logOut}>Logout</button>
     ) : (
       <button onClick={logIn}>Login</button>
     )}
     <Routes>
       <Route path='/' element={<Home />} />
       <Route path='/profile'
         element={
           <Protected isLoggedIn={isLoggedIn}>
             <Profile />
           </Protected>
         }
       />
       <Route path ='/about' element={<About />} />
     </Routes>
   </div>
   </BrowserRouter>
 );
}
export default App;

That's it on creating protected routes. You can now access the profile page only if you are logged in. If you try to navigate to the profile page without logging in you will be redirected to the home page.

Role-Based Access Control

Protected routes in React help you restrict unauthenticated users from accessing a page. This is very basic and in some cases, you might need to go even further and restrict users based on their roles. For instance, you can have a page say an analytics page that only grants access to admins. Here, you will need to add logic in the Protected component to check whether a user needs access to that route based on their role.