As we are done with our 👉Backend to set up frontend for MERN Stack Application, Let's use Vite framework for building react application.

So, Inorder to complete the steps of our Stock Management System we need to build the frontend that aligns the below requirements.

Plan

1.In your Main Project Directory open terminal and follow the steps.

# Create a Vite project
npm create vite@latest stock-management-frontend --template react

# Navigate to the project directory
cd stock-management-frontend

# Install dependencies
npm install

2.Install required dependencies

npm install react-router-dom  //React Router for navigation

npm install axios  //Axios for API requests

npm install react-toastify  //React-Toastify for notifications

npm install chart.js react-chartjs-2 //Chart.js for stock trend 
visualization

npm install file-saver //FileSaver for CSV export:

npm install jwt-decode  //To decode JWT tokens
  1. For Styling install tailwindcss for Vite configuration.

  2. Set up Base API URL for Fetch Requests (api.js), Login page, Register page.

  • utils/api.js
import axios from "axios";

const BASE_URL = "http://localhost:5000/api"; // Change this for production

const api = axios.create({
  baseURL: BASE_URL,
  headers: {
    "Content-Type": "application/json",
  },
});

export default api;

pages/Register.jsx - create a registration form as per our backend requriements.

  1. Defining the register component.
  2. State Management of the fields.
  3. Handling the form Submission.
  4. Creating UserInterface as per your styling requirements.
import { useState } from "react";
import { useNavigate } from "react-router-dom";
import api from "../utils/api"; // Import the centralized API instance

const Register = () => {
  const [name, setName] = useState("");
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const [role, setRole] = useState("user");
  const navigate = useNavigate();

  const handleRegister = async (e) => {
    e.preventDefault(); //prevents default form submission.
    try {
      await api.post("/auth/register", { name, email, password, role });
      navigate("/login");
    } catch (error) {
      alert(error.response?.data?.message || "Registration failed!");
    }
  };

  return (
    
      
        Register
         setName(e.target.value)} 
        />
         setEmail(e.target.value)} 
        />
         setPassword(e.target.value)} 
        />
         setRole(e.target.value)}
        >
          User
          Admin
        
        
          Register
        
      
    
  );
};

export default Register;
  • pages/Login.jsx - Create a Login form as per our backend requriements.
  1. State Management of required fields.
  2. Accessing the Login Function from AuthContext
  3. Navigation After Successful Login
  4. Handling the Login submission
  5. User Interface
import { useState, useContext } from "react";
import { useNavigate } from "react-router-dom";
import { AuthContext } from "../context/AuthContext";

const Login = () => {
  const [email, setEmail] = useState("");
  const [password, setPassword] = useState("");
  const { login } = useContext(AuthContext);
  const navigate = useNavigate();

  const handleLogin = async (e) => {
    e.preventDefault();
    try {
      await login(email, password);
      navigate("/dashboard");
    } catch (error) {
      alert(error.message);
    }
  };

  return (
    
      
        Login
         setEmail(e.target.value)}
        />
         setPassword(e.target.value)}
        />
        Login
      
    
  );
};

export default Login;

5.Setup Authentication in frontend using AuthCotext.

The common issues I faced everytime like,

  • Login persists only on the first login, but logs out on page refresh.

  • Private routes might not be handling the user state correctly.

  • The dashboard appears empty after login due to a delay in fetching user data.

  • Token might not be set correctly before fetching user data.

🔧 Fixed Code

  • AuthProvider (Fix User Persistence on Refresh)
  1. Importing Required Dependencies

  2. Creating the Authentication Context
    -Creates AuthContext, allowing components to access authentication data without prop drilling.

  3. Defining the AuthProvider Component
    -This component wraps the entire application, providing authentication state to all components.

  4. Defining the State Variables
    -user: Stores authenticated user details.
    -token: Stores the authentication token, initially retrieved from localStorage if available.
    -loading: Indicates whether authentication actions are in progress (useful for UI handling).

5.Handling Token Changes & User Authentication (useEffect)
-Runs whenever token changes.
-If a valid token exists, it:
Sets the token in Axios headers for all requests.
Calls fetchUser() to get user details.
If no token exists, it:
-Removes the Authorization header.
-Logs the user out by setting user to null.
-Stops loading.

6.Fetching User Details (fetchUser)

7.Handling Login (login function)

8.Handling Logout (logout function)

9.Providing Authentication Context to Components

import { createContext, useState, useEffect } from "react";
import api from "../utils/api";

export const AuthContext = createContext();

const AuthProvider = ({ children }) => {
  const [user, setUser] = useState(null);
  const [token, setToken] = useState(localStorage.getItem("token") || "");
  const [loading, setLoading] = useState(true);

  useEffect(() => {
    console.log("Token in AuthProvider:", token);

    if (token) {
      api.defaults.headers.common["Authorization"] = `Bearer ${token}`;
      fetchUser();
    } else {
      delete api.defaults.headers.common["Authorization"];
      setUser(null);
      setLoading(false);
    }
  }, [token]);

  const fetchUser = async () => {
    try {
      const token = localStorage.getItem("token");
      if (!token) {
        console.error("No token found, logging out...");
        logout();
        return;
      }

      console.log("Fetching user with token:", token);
      const { data } = await api.get("/auth/me", {
        headers: { Authorization: `Bearer ${token}` },
      });

      console.log("User fetched successfully:", data);
      setUser(data);
    } catch (error) {
      console.error("Failed to fetch user:", error.response?.data || error.message);
      logout();
    } finally {
      setLoading(false); // ✅ Ensures loading stops
    }
  };

  const login = async (email, password) => {
    try {
      const { data } = await api.post("/auth/login", { email, password });
      setToken(data.token);
      setUser(data.user);
      localStorage.setItem("token", data.token);
      api.defaults.headers.common["Authorization"] = `Bearer ${data.token}`;
    } catch (error) {
      throw new Error(error.response?.data?.message || "Login failed");
    }
  };

  const logout = () => {
    setToken("");
    setUser(null);
    localStorage.removeItem("token");
    delete api.defaults.headers.common["Authorization"];
    setLoading(false); // ✅ Stop loading after logout
  };

  return (
    
      {children}
    
  );
};

export default AuthProvider;
  1. PrivateRoute (Prevent Redirect on Refresh Before Auth Check) - used to protect routes that require authentication.
import { Navigate } from "react-router-dom";
import { useContext } from "react";
import { AuthContext } from "../context/AuthContext";

const PrivateRoute = ({ children }) => { //Represents the protected component/page.
  const { user, loading } = useContext(AuthContext);

  if (loading) return Loading...; // Prevents navigation issues

  return user ? {children} : ;
};

export default PrivateRoute;
  1. Dashboard (Fix Logout Handling)
import { useContext } from "react";
import { AuthContext } from "../context/AuthContext";

const Dashboard = () => {
  const { user } = useContext(AuthContext);

  if (!user) {
    return Loading...;
  }

  return (
    
        Dashboard
      Welcome, {user.name}
      Email: {user.email}
      Role: {user.role}
    
  );
};

export default Dashboard;
  • App.jsx - Update the main component for setting the frontend routes for navigation.
import React from "react";
import { BrowserRouter as Router, Routes, Route } from "react-router-dom";
import AuthProvider from "./context/AuthContext";
import Login from "./pages/Login";
import Dashboard from "./pages/Dashboard";
import PrivateRoute from "./routes/PrivateRoute";
import Register from "./pages/Register";

const App = () => {
  return (
    
      
        
          } />
          } />
          }>
            } /> 
          
        
      
    
  );
};

export default App;

So, after login the result page of the dashboard will be up like,

output

Hurray!🎉 We are done with auth based and private route based setup in frontend which is 50% completion of our application.

Let's continue further in upcoming blog post.Reach out in comment section for any queires.

Happy developing!

                      Let's grow together

upvote