Student Registration App: MERN
MERN Student Registration CRUD App
Features
Add Student
View Students
Edit Student
Update Student
Delete Student
Search Student
Email Validation
Phone Number Validation
In this project, we will build a Student Registration System using the MERN Stack, which includes MongoDB, Express.js, React, and Node.js. We will create a full-stack application that performs CRUD operations (Create, Read, Update, Delete) and handles data between frontend and backend.
Prerequisites:
Before starting, make sure you have:
- Basic knowledge of HTML, CSS, and JavaScript
Course link: Web Development : From Fundamentals to Advanced
- Understanding of React basics (components, state)
- Node.js and npm installed
- MongoDB installed and running (or MongoDB Atlas)
- A code editor like VS Code
This project will help you understand how a real-world full-stack application works from UI to database.
Step by Step Procedure:
Step 1: Create Project Folder
mkdir 2_Student_Registration_App
cd 2_Student_Registration_App
Step 2: Create Backend and Frontend Folders
mkdir backend frontend
Backend Setup
Step 3: Go Inside Backend Folder
cd backend
Step 4: Initialize Node.js Project
npm init -y
Step 5: Install Backend Packages
npm install express mongoose cors dotenv
Step 6: Install Nodemon
npm install nodemon --save-dev
Step 7: Create Backend Folder Structure
mkdir models
Create files:
backend
├── models
│ └── Student.js
├── server.js
├── .env
└── package.json
⚠️ Important: File name should be Student.js, not Student.json.
Step 8: Create .env
Create backend/.env
PORT=5000
MONGO_URI=mongodb://127.0.0.1:27017/student_registration_db
Step 9: Update package.json
In backend/package.json, update only the scripts section:
"scripts": {
"start": "node server.js",
"dev": "nodemon server.js"
}
Step 10: Create Student Model
Create backend/models/Student.js
const mongoose = require("mongoose");
const studentSchema = new mongoose.Schema(
{
name: {
type: String,
required: [true, "Student name is required"],
trim: true,
minlength: [3, "Name must be at least 3 characters"],
},
email: {
type: String,
required: [true, "Email is required"],
trim: true,
lowercase: true,
match: [
/^[^\s@]+@[^\s@]+\.[^\s@]+$/,
"Please enter a valid email address",
],
},
phone: {
type: String,
required: [true, "Phone number is required"],
trim: true,
match: [/^[0-9]{10}$/, "Phone number must be exactly 10 digits"],
},
course: {
type: String,
required: [true, "Course name is required"],
trim: true,
minlength: [2, "Course name must be at least 2 characters"],
},
city: {
type: String,
required: [true, "City is required"],
trim: true,
minlength: [2, "City name must be at least 2 characters"],
},
},
{
timestamps: true,
}
);
const Student = mongoose.model("Student", studentSchema);
module.exports = Student;
Step 11: Create Backend Server
Create backend/server.js
require("dotenv").config();
const express = require("express");
const mongoose = require("mongoose");
const cors = require("cors");
const Student = require("./models/Student");
const app = express();
// Middleware
app.use(cors());
app.use(express.json());
// Environment Variables
const PORT = process.env.PORT || 5000;
const MONGO_URI = process.env.MONGO_URI;
// MongoDB Connection
if (!MONGO_URI) {
console.log("MongoDB Error: MONGO_URI is missing in .env file");
} else {
mongoose
.connect(MONGO_URI)
.then(() => console.log("MongoDB Connected Successfully"))
.catch((error) => console.log("MongoDB Error:", error.message));
}
// Test Route
app.get("/", (req, res) => {
res.send("Student Registration Backend Running");
});
// CREATE Student
app.post("/api/students", async (req, res) => {
try {
const { name, email, phone, course, city } = req.body;
const newStudent = new Student({
name,
email,
phone,
course,
city,
});
await newStudent.save();
res.status(201).json({
message: "Student added successfully",
student: newStudent,
});
} catch (error) {
res.status(400).json({
message: "Error adding student",
error: error.message,
});
}
});
// READ All Students
app.get("/api/students", async (req, res) => {
try {
const students = await Student.find().sort({ createdAt: -1 });
res.status(200).json(students);
} catch (error) {
res.status(500).json({
message: "Error fetching students",
error: error.message,
});
}
});
// UPDATE Student
app.put("/api/students/:id", async (req, res) => {
try {
const { name, email, phone, course, city } = req.body;
const updatedStudent = await Student.findByIdAndUpdate(
req.params.id,
{
name,
email,
phone,
course,
city,
},
{
new: true,
runValidators: true,
}
);
if (!updatedStudent) {
return res.status(404).json({
message: "Student not found",
});
}
res.status(200).json({
message: "Student updated successfully",
student: updatedStudent,
});
} catch (error) {
res.status(400).json({
message: "Error updating student",
error: error.message,
});
}
});
// DELETE Student
app.delete("/api/students/:id", async (req, res) => {
try {
const deletedStudent = await Student.findByIdAndDelete(req.params.id);
if (!deletedStudent) {
return res.status(404).json({
message: "Student not found",
});
}
res.status(200).json({
message: "Student deleted successfully",
});
} catch (error) {
res.status(500).json({
message: "Error deleting student",
error: error.message,
});
}
});
// Start Server
app.listen(PORT, () => {
console.log(`Server running on port ${PORT}`);
});
Step 12: Run Backend
npm run dev
Expected output:
MongoDB Connected Successfully
Server running on port 5000
Backend URL:
http://localhost:5000
Frontend Setup
Step 13: Open New Terminal and Go to Frontend Folder
cd 2_Student_Registration_App/frontend
Or from backend folder:
cd ../frontend
Step 14: Create React App Using Vite
npm create vite@latest .
Choose:
React
JavaScript
Step 15: Install Frontend Dependencies
npm install
Step 16: Install Axios
npm install axios
Step 17: Update App.jsx
Replace frontend/src/App.jsx
import { useEffect, useState } from "react";
import axios from "axios";
import "./App.css";
const API_URL = "http://localhost:5000/api/students";
function App() {
const [formData, setFormData] = useState({
name: "",
email: "",
phone: "",
course: "",
city: "",
});
const [students, setStudents] = useState([]);
const [editingId, setEditingId] = useState(null);
const [search, setSearch] = useState("");
const fetchStudents = async () => {
try {
const response = await axios.get(API_URL);
setStudents(response.data);
} catch (error) {
console.log("Error fetching students:", error);
}
};
useEffect(() => {
fetchStudents();
}, []);
const handleChange = (e) => {
const { name, value } = e.target;
if (name === "phone") {
const onlyNumbers = value.replace(/[^0-9]/g, "");
setFormData({
...formData,
[name]: onlyNumbers,
});
} else {
setFormData({
...formData,
[name]: value,
});
}
};
const clearForm = () => {
setFormData({
name: "",
email: "",
phone: "",
course: "",
city: "",
});
setEditingId(null);
};
const handleSubmit = async (e) => {
e.preventDefault();
if (formData.phone.length !== 10) {
alert("Phone number must be exactly 10 digits");
return;
}
try {
if (editingId) {
await axios.put(`${API_URL}/${editingId}`, formData);
alert("Student updated successfully");
} else {
await axios.post(API_URL, formData);
alert("Student added successfully");
}
clearForm();
fetchStudents();
} catch (error) {
console.log("Error saving student:", error);
if (error.response && error.response.data && error.response.data.error) {
alert(error.response.data.error);
} else {
alert("Something went wrong");
}
}
};
const handleEdit = (student) => {
setEditingId(student._id);
setFormData({
name: student.name,
email: student.email,
phone: student.phone,
course: student.course,
city: student.city,
});
};
const handleDelete = async (id) => {
const confirmDelete = window.confirm(
"Are you sure you want to delete this student?"
);
if (!confirmDelete) {
return;
}
try {
await axios.delete(`${API_URL}/${id}`);
alert("Student deleted successfully");
fetchStudents();
} catch (error) {
console.log("Error deleting student:", error);
alert("Something went wrong");
}
};
const filteredStudents = students.filter((student) => {
return (
student.name.toLowerCase().includes(search.toLowerCase()) ||
student.email.toLowerCase().includes(search.toLowerCase()) ||
student.phone.toLowerCase().includes(search.toLowerCase()) ||
student.course.toLowerCase().includes(search.toLowerCase()) ||
student.city.toLowerCase().includes(search.toLowerCase())
);
});
return (
<div className="app">
<header className="header">
<h1>VMS CLASS</h1>
<p>Student Registration Management System</p>
</header>
<main className="dashboard">
<section className="card form-card">
<h2>{editingId ? "Update Student" : "Register Student"}</h2>
<form onSubmit={handleSubmit}>
<input
type="text"
name="name"
placeholder="Enter Student Name"
value={formData.name}
onChange={handleChange}
minLength="3"
required
/>
<input
type="email"
name="email"
placeholder="Enter Email Address"
value={formData.email}
onChange={handleChange}
required
/>
<input
type="tel"
name="phone"
placeholder="Enter 10 Digit Phone Number"
value={formData.phone}
onChange={handleChange}
pattern="[0-9]{10}"
maxLength="10"
title="Phone number must be exactly 10 digits"
required
/>
<input
type="text"
name="course"
placeholder="Enter Course Name"
value={formData.course}
onChange={handleChange}
minLength="2"
required
/>
<input
type="text"
name="city"
placeholder="Enter City"
value={formData.city}
onChange={handleChange}
minLength="2"
required
/>
<button type="submit" className="submit-btn">
{editingId ? "Update Student" : "Add Student"}
</button>
{editingId && (
<button type="button" className="cancel-btn" onClick={clearForm}>
Cancel
</button>
)}
</form>
</section>
<section className="card table-card">
<div className="table-header">
<div>
<h2>Registered Students</h2>
<p>Total Students: {students.length}</p>
</div>
<input
type="text"
placeholder="Search student..."
value={search}
onChange={(e) => setSearch(e.target.value)}
className="search-box"
/>
</div>
<div className="table-wrapper">
<table>
<thead>
<tr>
<th>Sl No</th>
<th>Name</th>
<th>Email</th>
<th>Phone</th>
<th>Course</th>
<th>City</th>
<th>Actions</th>
</tr>
</thead>
<tbody>
{filteredStudents.length > 0 ? (
filteredStudents.map((student, index) => (
<tr key={student._id}>
<td>{index + 1}</td>
<td>{student.name}</td>
<td>{student.email}</td>
<td>{student.phone}</td>
<td>{student.course}</td>
<td>{student.city}</td>
<td>
<button
className="edit-btn"
onClick={() => handleEdit(student)}
>
Edit
</button>
<button
className="delete-btn"
onClick={() => handleDelete(student._id)}
>
Delete
</button>
</td>
</tr>
))
) : (
<tr>
<td colSpan="7" className="no-data">
No students found
</td>
</tr>
)}
</tbody>
</table>
</div>
</section>
</main>
</div>
);
}
export default App;
Step 18: Update App.css
Replace frontend/src/App.css
* {
box-sizing: border-box;
}
body {
margin: 0;
font-family: Arial, sans-serif;
background: #eef2f7;
color: #222;
}
.app {
min-height: 100vh;
}
.header {
background: linear-gradient(135deg, #101820, #1f4068);
color: white;
text-align: center;
padding: 35px 20px;
}
.header h1 {
margin: 0;
font-size: 42px;
color: #00ff99;
letter-spacing: 2px;
}
.header p {
margin-top: 8px;
font-size: 20px;
color: #ffd700;
}
.dashboard {
width: 95%;
max-width: 1300px;
margin: 30px auto;
display: grid;
grid-template-columns: 350px 1fr;
gap: 25px;
}
.card {
background: white;
border-radius: 16px;
padding: 25px;
box-shadow: 0 10px 25px rgba(0, 0, 0, 0.08);
}
.form-card h2,
.table-card h2 {
margin-top: 0;
color: #1f4068;
}
form {
display: flex;
flex-direction: column;
gap: 14px;
}
input {
padding: 13px;
border: 1px solid #ccd3dd;
border-radius: 10px;
font-size: 15px;
outline: none;
}
input:focus {
border-color: #00b894;
}
.submit-btn,
.cancel-btn,
.edit-btn,
.delete-btn {
border: none;
padding: 11px 14px;
border-radius: 9px;
cursor: pointer;
font-weight: bold;
}
.submit-btn {
background: #00b894;
color: white;
}
.submit-btn:hover {
background: #019875;
}
.cancel-btn {
background: #636e72;
color: white;
}
.table-header {
display: flex;
justify-content: space-between;
align-items: center;
gap: 12px;
margin-bottom: 20px;
}
.table-header p {
margin: 5px 0 0;
color: #666;
}
.search-box {
width: 250px;
}
.table-wrapper {
overflow-x: auto;
}
table {
width: 100%;
border-collapse: collapse;
min-width: 850px;
}
th {
background: #1f4068;
color: white;
padding: 13px;
text-align: left;
}
td {
padding: 13px;
border-bottom: 1px solid #e5e7eb;
}
tr:hover {
background: #f8fafc;
}
.edit-btn {
background: #0984e3;
color: white;
margin-right: 8px;
}
.delete-btn {
background: #d63031;
color: white;
}
.no-data {
text-align: center;
padding: 25px;
color: #777;
}
@media (max-width: 900px) {
.dashboard {
grid-template-columns: 1fr;
}
.search-box {
width: 100%;
}
.table-header {
flex-direction: column;
align-items: stretch;
}
}
Step 19: Run Backend
Open terminal inside backend:
cd backend
npm run dev
Step 20: Run Frontend
Open another terminal inside frontend:
cd frontend
npm run dev
Open:
http://localhost:5173
Validation Added
Frontend HTML Validation
Name minimum 3 characters
Email must be valid email format
Phone must be exactly 10 digits
Course minimum 2 characters
City minimum 2 characters
Backend Mongoose Validation
Email format validation
Phone number must be exactly 10 digits
Required fields validation
Minimum length validation
API Routes
POST /api/students Add Student
GET /api/students View Students
PUT /api/students/:id Update Student
DELETE /api/students/:id Delete Student
Final Project Flow
React Form
↓
HTML Validation
↓
Axios API Request
↓
Express Backend
↓
Mongoose Validation
↓
MongoDB Database
↓
Response Back to React
↓
Student Table Updated
Summary
This project is a complete MERN Stack CRUD application for managing student registrations.
Users can add, view, update, delete, and search student data through a React frontend connected to a Node.js + Express backend.
MongoDB stores the data using Mongoose, with validations applied on both frontend and backend.
It demonstrates the full data flow from UI → API → database → and back to UI.
Keywords
mern stack project
mern stack crud project
student registration project
student management system
react node project
react crud app
node js crud project
mongodb project
express js project
full stack project
web development project
react project for beginners
node js project for beginners
mern stack tutorial
crud application example