Project Structure(Frontend)
The updated dependencies in package.json file will look like:
"dependencies": {
"@emotion/react": "^11.11.4",
"@emotion/styled": "^11.11.0",
"@mui/icons-material": "^5.15.12",
"@mui/material": "^5.15.12",
"@testing-library/jest-dom": "^5.17.0",
"@testing-library/react": "^13.4.0",
"@testing-library/user-event": "^13.5.0",
"axios": "^1.6.7",
"react": "^18.2.0",
"react-dom": "^18.2.0",
"react-router-dom": "^6.22.3",
"react-scripts": "5.0.1",
"react-toastify": "^10.0.4",
"web-vitals": "^2.1.4"
}
Step 4: Create a .env file to hide your secret key(s) or variable(s).
REACT_APP_BACKEND_URL=http://localhost:4000
Step 5: Create the required files as seen in the folder structure and add the following codes.
//App.js
import "./App.css";
import {
BrowserRouter,
Navigate,
Route,
Routes
} from "react-router-dom";
import Login from "./pages/Login";
import ForgotPassword from "./pages/ForgotPassword";
import ResetPassword from "./pages/ResetPassword";
import { ToastContainer } from "react-toastify";
import "react-toastify/dist/ReactToastify.min.css";
const App = () => {
return (
<div className="App">
<ToastContainer />
<BrowserRouter>
<Routes>
<Route path="/"
element={<Navigate to="/login" />} />
<Route path="/login" element={<Login />} />
<Route path="forgotPassword"
element={<ForgotPassword />} />
<Route path="resetPassword"
element={<ResetPassword />} />
</Routes>
</BrowserRouter>
</div>
);
};
export default App;
//Login.js
import { React } from "react";
import Avatar from "@mui/material/Avatar";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import Link from "@mui/material/Link";
import Grid from "@mui/material/Grid";
import Box from "@mui/material/Box";
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
import Typography from "@mui/material/Typography";
import Container from "@mui/material/Container";
import {
Card,
CardContent
} from "@mui/material";
const Login = () => {
const handleSubmit = (e) => {
e.preventDefault();
const data = new FormData(e.currentTarget);
console.log({
email: data.get("email"),
password: data.get("password"),
});
};
return (
<Container maxWidth="sm">
<Box
sx={{
marginTop: 10,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<Card sx={{ boxShadow: "4" }}>
<CardContent sx={{ m: 3 }}>
<Avatar sx={{
m: "auto",
bgcolor: "primary.main"
}}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1" variant="h5">
Login
</Typography>
<Box
component="form"
onSubmit={handleSubmit}
noValidate
sx={{ mt: 1 }}
>
<TextField
margin="normal"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
autoFocus
/>
<TextField
margin="normal"
required
fullWidth
name="password"
label="Password"
type="password"
id="password"
autoComplete="current-password"
/>
<Button
type="submit"
fullWidth
variant="contained"
sx={{ mt: 3, mb: 2 }}
>
Sign In
</Button>
<Grid container>
<Grid item sm>
<Link
href="/forgotPassword"
variant="body2"
style={{textDecoration:"None"}}
>
Forgot password?
</Link>
</Grid>
</Grid>
</Box>
</CardContent>
</Card>
</Box>
</Container>
);
};
export default Login;
//ForgotPassword.js
import { React } from "react";
import axios from "axios";
import Avatar from "@mui/material/Avatar";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import Box from "@mui/material/Box";
import LockOutlinedIcon from "@mui/icons-material/LockOutlined";
import Typography from "@mui/material/Typography";
import Container from "@mui/material/Container";
import {
Card,
CardContent
} from "@mui/material";
import { toast } from "react-toastify";
const ForgotPassword = () => {
const handleSubmit = async (e) => {
e.preventDefault();
const data = new FormData(e.currentTarget);
const email = data.get("email");
const url = process
.env
.REACT_APP_BACKEND_URL + "/api/forgotPassword";
const res = await axios.post(url, { email: email });
if (res.data.success === false) {
toast.error(res.data.message, {
autoClose: 5000,
position: "top-right",
});
} else {
toast.success(res.data.message, {
autoClose: 5000,
position: "top-right",
});
}
};
return (
<Container maxWidth="sm">
<Box
sx={{
marginTop: 10,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<Card sx={{ boxShadow: "4" }}>
<CardContent sx={{ m: 3 }}>
<Avatar sx={{
m: "auto",
bgcolor: "primary.main"
}}>
<LockOutlinedIcon />
</Avatar>
<Typography component="h1"
variant="h5" sx={{ mt: 1 }}>
Forgot Password
</Typography>
<Box component="form"
onSubmit={handleSubmit} sx={{ mt: 1 }}>
<TextField
margin="normal"
required
fullWidth
id="email"
label="Email Address"
name="email"
autoComplete="email"
autoFocus
/>
<Button
type="submit"
fullWidth
variant="contained"
sx={{ mt: 3, mb: 2 }}
>
Reset Password
</Button>
</Box>
</CardContent>
</Card>
</Box>
</Container>
);
};
export default ForgotPassword;
//ResetPassword.js
import { React } from "react";
import {
useSearchParams,
useNavigate
} from "react-router-dom";
import axios from "axios";
import Avatar from "@mui/material/Avatar";
import Button from "@mui/material/Button";
import TextField from "@mui/material/TextField";
import Box from "@mui/material/Box";
import LockResetIcon from "@mui/icons-material/LockReset";
import Typography from "@mui/material/Typography";
import Container from "@mui/material/Container";
import {
Card,
CardContent
} from "@mui/material";
import { toast } from "react-toastify";
const ResetPassword = () => {
const [searchParams] = useSearchParams();
let navigate = useNavigate();
const userId = searchParams.get("id");
const token = searchParams.get("token");
const handleSubmit = async (e) => {
e.preventDefault();
const data = new FormData(e.currentTarget);
const newpassword = data.get("newpassword");
const confirmpassword = data.get("confirmpassword");
if (newpassword !== confirmpassword)
toast.error(`New Password and
Confirm Password do not match !`, {
autoClose: 5000,
position: "top-right",
});
else {
const url = process.env.REACT_APP_BACKEND_URL
+ "/api/resetPassword";
const res = await axios.post(url, {
password: newpassword,
token: token,
userId: userId,
});
if (res.data.success === false) {
toast.error(res.data.message, {
autoClose: 5000,
position: "top-right",
});
} else {
toast.success(res.data.message, {
autoClose: 5000,
position: "top-right",
});
setTimeout(() => {
navigate("/login");
}, 2000);
}
}
};
return (
<Container maxWidth="sm">
<Box
sx={{
marginTop: 10,
display: "flex",
flexDirection: "column",
alignItems: "center",
}}
>
<Card sx={{ boxShadow: "4" }}>
<CardContent sx={{ m: 3 }}>
<Avatar sx={{ m: "auto",
bgcolor: "primary.main" }}>
<LockResetIcon />
</Avatar>
<Typography component="h1"
variant="h5"
sx={{ mt: 1 }}>
Reset Password
</Typography>
<Box component="form"
onSubmit={handleSubmit}
sx={{ mt: 1 }}>
<TextField
margin="normal"
required
fullWidth
type="password"
name="newpassword"
id="newpassword"
label="New Password"
autoFocus
/>
<TextField
margin="normal"
required
fullWidth
type="password"
name="confirmpassword"
id="confirmpassword"
label="Confirm Password"
/>
<Button
type="submit"
fullWidth
variant="contained"
sx={{ mt: 3, mb: 2 }}
>
Submit
</Button>
</Box>
</CardContent>
</Card>
</Box>
</Container>
);
};
export default ResetPassword;
Step 6: To start the frontend run the following command:
npm start
Forgot & Reset Password Feature with React and Node JS
Almost all the websites that you use nowadays require you to signup and then login. But, with the tremendous rise in number of websites being used by you, it is difficult for you to remember the passwords for so many websites if you use different passwords on different websites.
Ever had that “Oh no, I forgot my password” moment? Fear not! The “Forgot and Reset Password” feature on these website acts as a life saver for you. So, the next time you draw a blank on your password, just click that “Forgot Password” link and reset your password. In this article, we will look at how we can implement this most important funcationality in any website i.e. Forgot and Reset Password functionality using React and NodeJS.
Output Preview: Let us have a look at how the final output will look like