DEV Community

Cover image for Private Route
Himanshu Baghel
Himanshu Baghel

Posted on

Private Route

How can we protect our routes from unauthorized access? Let's learn about it.

Let's start from scratch.
So first create a react app

npx create-react-app your-project-name
Enter fullscreen mode Exit fullscreen mode

Or

yarn create vite your-project-name
Enter fullscreen mode Exit fullscreen mode

I am using yarn vite here

After running the above yarn command, you need to select two things:

  1. Select framework
  2. Select variant(language)

After that, open VS Code and run following command:

yarn 
yarn add axios
Enter fullscreen mode Exit fullscreen mode

Or

npm install
npm install axios
Enter fullscreen mode Exit fullscreen mode

Then run your app

yarn dev
Enter fullscreen mode Exit fullscreen mode

or

npm start
Enter fullscreen mode Exit fullscreen mode

Now, let's create the login page and home page.

//Login.jsx
import React, { useState } from "react";
import { Button } from "react-bootstrap";
import "./commonStyle.css";

const Login = () => {
  const [userName, setUserName] = useState("");
  const [password, setPassword] = useState("");

  const handleLogin = () => {
    const data = {
      username: userName,
      pass: password,
    };
    const dataString = JSON.stringify(data);
    sessionStorage.setItem("Status", dataString);
  };

  return (
    <div className="Cont">
      <div className="homeCont ">
        <h2>Login</h2>
        <input
          type="text"
          className="inputStyle"
          value={userName}
          onChange={(e) => setUserName(e.target.value)}
          placeholder="Enter your username"
        />
        <input
          type="password"
          className="inputStyle"
          value={password}
          onChange={(e) => setPassword(e.target.value)}
          placeholder="Enter your password"
        />
        <Button
          className={password.length < 8 ? "disableBtn" : "btn"}
          onClick={handleLogin}
        >
          Login
        </Button>
      </div>
    </div>
  );
};

export default Login;

Enter fullscreen mode Exit fullscreen mode

Along with create Css file

/* commonStyle.css */
.Cont{
    height: 100vh;
    width: 100%;
    display: flex;
    justify-content: center;
    align-items: center;
    background-color: #535C91;
    color: #fff;
}
.homeCont{
    background-color: #070F2B;
    border: 1px solid #1a285b;
    height: 40vh;
    width: 30vw;
    border-radius: 5px;
    display: flex;
    flex-direction: column;
align-items: center;
justify-content: center;
}
.inputStyle{
    width: 80%;
    height: 50px;
    margin: 10px 0;
    background-color: #191859;
    border: 1px solid #3a3960;
    border-radius: 5px;
    color: #fff;
    font-size: 15px;
    padding: 0 7px;
}
.inputStyle::placeholder{
    color: #a7a7a7;

}

.inputStyle:focus{
    outline: none;
}
.btn{
    background-color: #f61542;
    width: 80%;
    height: 40px;
    margin: 10px 0;
    color: #fff;
    font-size: 16px;
    border: none;
    border-radius: 50px;
    cursor: pointer;
}
.btn:hover{
    background-color: #f61542; 
}
.disableBtn{
    background-color: #df4d6a; 
    cursor: default;
}
.disableBtn:hover{
    background-color: #df4d6a; 
}
.bookCont{
    height: auto;
    padding-top: 10px;
}
.bookDtl{
    height: 30px;
    width: 100%;
    border: 1px solid #535C91;
    display: flex;
    flex-direction: row;
    justify-content: space-between;
    padding: 0 8px;
    align-items: center;
}
Enter fullscreen mode Exit fullscreen mode

And It's look like this

Login Page
Now, create the home page that we want to protect so that only authorized users can see it. In my case, users can access the home page only after logging in.

Note: On the Home page, I'm calling an API to add books, but it's up to you what you want to render on this page.

//home.jsx
import React from "react";
import { useEffect, useState } from "react";
import axios from "axios";
import "./commonStyle.css";
const Home = () => {
  const [bookData, setBookData] = useState([]);
  const [bookTitle, setBookTitle] = useState("");
  const [bookAuthor, setBookAuthor] = useState("");
  const [msg, setMsg] = useState("");

  const handleSubmit = async () => {
    const payload = {
      title: bookTitle,
      author: bookAuthor,
    };
    axios
      .post("http://localhost:3001/books", payload)
      .then((res) => {
        setMsg(res.data.message);
        fetchData();
      })
      .catch(() => {
        setMsg("Something went wrong");
      });
  };

  const fetchData = async () => {
    try {
      const response = await axios.get("http://localhost:3001/books");
      setBookData(response.data.data);
    } catch (error) {
      console.error("Error fetching data:", error);
    }
  };
  useEffect(() => {
    fetchData();
  }, []);

  return (
    <div className="Cont">
      <div className="homeCont bookCont">
        <h3>Book Details</h3>
        <input
          className="inputStyle"
          onCopy={(e) => e.preventDefault()}
          onPaste={(e) => e.preventDefault()}
          placeholder="Book Title"
          value={bookTitle}
          onChange={(e) => setBookTitle(e.target.value)}
          autoComplete="off" 
        />
        <input
          className="inputStyle"
          placeholder="Book Author"
          value={bookAuthor}
          autoComplete="off" 
          onCopy={(e) => e.preventDefault()}
          onPaste={(e) => e.preventDefault()}
          onChange={(e) => setBookAuthor(e.target.value)}
        />
        <p>{msg}</p>
        <button className="btn" onClick={handleSubmit}>
          Add Book
        </button>
        {bookData.map((book) => {
          return (
            <div className="bookDtl" key={book.id}>
              <p> {book.id}</p>
              <p>{book.title}</p>
              <p>{book.author}</p>
            </div>
          );
        })}
      </div>
    </div>
  );
};

export default Home;

Enter fullscreen mode Exit fullscreen mode

Now come our main page App.jsx

//App.jsx

import './App.css'
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom'
import Home from './Screens/Home'
import PrivateRoute from './Screens/PrivateRoute'
import Login from './Screens/Login'


function App() {
  return (
    <Router>
      <Routes>
        <Route path='/' element={<Login />} exact />
        <Route element={<PrivateRoute />}>
          <Route path='/home' element={<Home />} />
        </Route>
      </Routes>
    </Router>
  )
}

export default App

Enter fullscreen mode Exit fullscreen mode

Here, I have created a simple route using the react-router-dom library. Additionally, I have defined my private routes inside this setup

  <Route element={<PrivateRoute />}> </Route>
Enter fullscreen mode Exit fullscreen mode

So here, I am protecting only one route, i.e., the home route, but you can protect as many as you want.
After that, create the Private Route page, which we are importing on the App page.

//PrivateRoute.jsx

import React from 'react'
import { Navigate, Outlet } from 'react-router-dom'

const PrivateRoute = ({ children, ...rest }) => {
    const data = sessionStorage.getItem('Status');
    const isLogin = JSON.parse(data);
    return (
        isLogin ?
            <Outlet />
            :
            <Navigate to='/' />
    )

}

export default PrivateRoute
Enter fullscreen mode Exit fullscreen mode
  1. We're using the ...rest parameter in the PrivateRoute component's props. This allows the component to accept any additional props that are not explicitly destructured (children in this case).
  2. The rest props are then passed down to the underlying component using the spread operator (...rest).
  3. This flexibility allows you to configure the route further by passing props such as exact, path, component, render, or any custom props you may need directly to the component within PrivateRoute. e.g.
 <PrivateRoute path="/about" element={<About />} exact />
Enter fullscreen mode Exit fullscreen mode

"Here, I check if the user is logged in, then create an object inside the sessionStorage and check this during private route. If login data is available in sessionStorage, then the user can go to the home page; otherwise, the user can't go to the home page.

An should be used in parent route elements to render their child route elements. This allows nested UI to show up when child routes are rendered. If the parent route matched exactly, it will render a child index route or nothing if there is no index route.

*Thank you ❤️ *

Top comments (0)