Lesson 59 of 60 – React Mini Project
98%

React Mini Project

In this lesson, we will build a simple React Student Management Dashboard. This mini project combines important React concepts learned throughout the tutorial, including components, props, state, events, forms, lists, conditional rendering, and reusable UI.

Project Goal: Build a simple student management application where users can add students, display student records, search students, and delete students.

1. Project Overview

Our project will contain a simple student dashboard.

Student Management App
        ↓
 ┌──────┼────────┐
 ↓      ↓        ↓
Add   Search   Student List
Student Students
        ↓
     Delete
     Student

This project demonstrates how different React concepts work together.

2. Project Features

  • Add a new student
  • Display students
  • Search students
  • Delete students
  • Show total student count
  • Validate required fields
  • Use reusable components
  • Manage application state

3. Suggested Project Structure

src/
├── components/
│   ├── Header.jsx
│   ├── StudentForm.jsx
│   ├── StudentList.jsx
│   └── StudentCard.jsx
│
├── App.jsx
└── main.jsx

Separating the application into components makes the project easier to understand and maintain.

4. Student Data Structure

Each student can be represented by an object.

const student = {
    id: 1,
    name: "Amit",
    email: "amit@example.com",
    course: "React"
};

The application can store multiple student objects inside an array.

5. Initial Student Data

const initialStudents = [
    {
        id: 1,
        name: "Amit",
        email: "amit@example.com",
        course: "React"
    },
    {
        id: 2,
        name: "Neha",
        email: "neha@example.com",
        course: "JavaScript"
    },
    {
        id: 3,
        name: "Rahul",
        email: "rahul@example.com",
        course: "Python"
    }
];

6. Creating Student State

The student list can be stored using useState.

const [students, setStudents] =
    useState(initialStudents);

Calling setStudents() updates the student list and causes the component to render again.

7. Creating the Search State

We can store the search text in another state variable.

const [search, setSearch] =
    useState("");

The value changes when the user types into the search box.

8. Creating the Student Form

The form can contain fields for student name, email, and course.

<form>

    <input
        type="text"
        placeholder="Student Name"
    />

    <input
        type="email"
        placeholder="Email"
    />

    <input
        type="text"
        placeholder="Course"
    />

    <button type="submit">
        Add Student
    </button>

</form>

9. Form State

The form fields can be controlled using a state object.

const [form, setForm] =
    useState({
        name: "",
        email: "",
        course: ""
    });

10. Updating Form Fields

A reusable change handler can update individual fields.

function handleChange(e) {

    const {
        name,
        value
    } = e.target;

    setForm({
        ...form,
        [name]: value
    });
}

11. Controlled Input

A controlled input gets its value from React state.

<input
    name="name"
    value={form.name}
    onChange={handleChange}
    placeholder="Student Name"
/>

React becomes the source of truth for the input value.

12. Handling Form Submission

The form submission handler can prevent the browser's default page reload.

function handleSubmit(e) {

    e.preventDefault();

    // Add student here

}

13. Form Validation

Before adding a student, required fields should be checked.

if (
    !form.name ||
    !form.email ||
    !form.course
) {

    setError(
        "All fields are required"
    );

    return;
}

14. Error State

The application can display validation errors using another state variable.

const [error, setError] =
    useState("");

When the form is valid, the error can be cleared.

setError("");

15. Adding a Student

After validation, a new student object can be added to the array.

const newStudent = {
    id: Date.now(),
    name: form.name,
    email: form.email,
    course: form.course
};

setStudents([
    ...students,
    newStudent
]);

16. Resetting the Form

After successfully adding a student, reset the form fields.

setForm({
    name: "",
    email: "",
    course: ""
});

This prepares the form for the next student.

17. Creating the Search Input

<input
    type="text"
    value={search}
    onChange={e =>
        setSearch(
            e.target.value
        )
    }
    placeholder="Search students"
/>

The search state changes as the user types.

18. Filtering Students

The student array can be filtered using the search text.

const filteredStudents =
    students.filter(student =>

        student.name
            .toLowerCase()
            .includes(
                search.toLowerCase()
            )

    );

19. Rendering Student List

The filtered student array can be displayed using map().

{filteredStudents.map(
    student => (

    <div key={student.id}>

        <h3>
            {student.name}
        </h3>

        <p>
            {student.email}
        </p>

        <p>
            {student.course}
        </p>

    </div>

))}

20. Creating StudentCard

A separate component can display one student.

function StudentCard({
    student,
    onDelete
}) {

    return (
        <div className="card mb-3">

            <div className="card-body">

                <h5>
                    {student.name}
                </h5>

                <p>
                    {student.email}
                </p>

                <p>
                    {student.course}
                </p>

                <button
                    className="btn btn-danger"
                    onClick={() =>
                        onDelete(
                            student.id
                        )
                    }
                >

                    Delete

                </button>

            </div>

        </div>
    );
}

21. Deleting a Student

The filter() method can create a new array without the selected student.

function handleDelete(id) {

    setStudents(
        students.filter(
            student =>
                student.id !== id
        )
    );
}

22. Passing Delete Function as a Prop

The parent can pass the delete function to the student component.

<StudentCard
    key={student.id}
    student={student}
    onDelete={handleDelete}
/>

This demonstrates communication from a child component back to the parent through a callback prop.

23. Showing Total Students

The number of students can be displayed using the array's length property.

<h3>
    Total Students:
    {students.length}
</h3>

24. Conditional Rendering

If there are no matching students, display a message.

{filteredStudents.length === 0 ? (

    <p>
        No students found.
    </p>

) : (

    filteredStudents.map(
        student => (
            <StudentCard
                key={student.id}
                student={student}
                onDelete={handleDelete}
            />
        )
    )

)}

25. Complete StudentCard Component

function StudentCard({
    student,
    onDelete
}) {

    return (
        <div className="card mb-3 shadow-sm">

            <div className="card-body">

                <h5 className="card-title">
                    {student.name}
                </h5>

                <p className="card-text">
                    Email:
                    {student.email}
                </p>

                <p className="card-text">
                    Course:
                    {student.course}
                </p>

                <button
                    className="btn btn-danger"
                    onClick={() =>
                        onDelete(
                            student.id
                        )
                    }
                >

                    Delete

                </button>

            </div>

        </div>
    );
}

export default StudentCard;

26. Complete StudentForm Component

import {
    useState
} from "react";

function StudentForm({
    onAdd
}) {

    const [form, setForm] =
        useState({
            name: "",
            email: "",
            course: ""
        });

    const [error, setError] =
        useState("");

    function handleChange(e) {

        const {
            name,
            value
        } = e.target;

        setForm({
            ...form,
            [name]: value
        });
    }

    function handleSubmit(e) {

        e.preventDefault();

        if (
            !form.name ||
            !form.email ||
            !form.course
        ) {

            setError(
                "All fields are required"
            );

            return;
        }

        setError("");

        onAdd(form);

        setForm({
            name: "",
            email: "",
            course: ""
        });
    }

    return (
        <form
            onSubmit={handleSubmit}
            className="mb-4"
        >

            {error && (

                <div className="alert alert-danger">
                    {error}
                </div>

            )}

            <input
                name="name"
                value={form.name}
                onChange={handleChange}
                placeholder="Student Name"
                className="form-control mb-2"
            />

            <input
                name="email"
                type="email"
                value={form.email}
                onChange={handleChange}
                placeholder="Email"
                className="form-control mb-2"
            />

            <input
                name="course"
                value={form.course}
                onChange={handleChange}
                placeholder="Course"
                className="form-control mb-2"
            />

            <button
                className="btn btn-primary"
                type="submit"
            >

                Add Student

            </button>

        </form>
    );
}

export default StudentForm;

27. Complete StudentList Component

import StudentCard
from "./StudentCard";

function StudentList({
    students,
    onDelete
}) {

    if (students.length === 0) {

        return (
            <p>
                No students found.
            </p>
        );
    }

    return (
        <div>

            {students.map(
                student => (

                <StudentCard
                    key={student.id}
                    student={student}
                    onDelete={onDelete}
                />

            ))}

        </div>
    );
}

export default StudentList;

The import should normally be written on one line:

import StudentCard from "./StudentCard";

28. Complete App Component

import {
    useState
} from "react";

import StudentForm
from "./components/StudentForm";

import StudentList
from "./components/StudentList";

function App() {

    const [students, setStudents] =
        useState([
            {
                id: 1,
                name: "Amit",
                email: "amit@example.com",
                course: "React"
            },
            {
                id: 2,
                name: "Neha",
                email: "neha@example.com",
                course: "JavaScript"
            }
        ]);

    const [search, setSearch] =
        useState("");

    function addStudent(student) {

        const newStudent = {
            ...student,
            id: Date.now()
        };

        setStudents([
            ...students,
            newStudent
        ]);
    }

    function deleteStudent(id) {

        setStudents(
            students.filter(
                student =>
                    student.id !== id
            )
        );
    }

    const filteredStudents =
        students.filter(student =>

            student.name
                .toLowerCase()
                .includes(
                    search.toLowerCase()
                )

        );

    return (
        <div className="container mt-4">

            <h1 className="mb-4">
                Student Management
            </h1>

            <StudentForm
                onAdd={addStudent}
            />

            <input
                className="form-control mb-3"
                placeholder="Search students"
                value={search}
                onChange={e =>
                    setSearch(
                        e.target.value
                    )
                }
            />

            <h3>
                Total Students:
                {students.length}
            </h3>

            <StudentList
                students={
                    filteredStudents
                }
                onDelete={
                    deleteStudent
                }
            />

        </div>
    );
}

export default App;

29. How the Mini Project Works

App
 ↓
StudentForm
 ↓
Add Student
 ↓
students State
 ↓
Filter by Search
 ↓
StudentList
 ↓
StudentCard
 ↓
Delete Student
 ↓
Update students State

This project combines several React concepts:

  • useState
  • Props
  • Callback functions
  • Controlled forms
  • Form validation
  • map()
  • filter()
  • Conditional rendering
  • Component composition
  • Event handling

30. Mini Project Best Practices

  • Keep components focused and reusable.
  • Keep student data in the appropriate parent state.
  • Use stable IDs for list keys.
  • Validate form data before adding students.
  • Use callback props for child-to-parent communication.
  • Keep search logic separate from display components.
  • Use conditional rendering for empty results.
  • Keep JSX readable.
  • Separate components into meaningful files.
  • For a real application, connect the frontend to a backend API and database instead of relying only on local component state.
React Components
       ↓
     State
       ↓
     Events
       ↓
      Props
       ↓
   UI Updates

📌 Key Points

  • A React mini project combines multiple concepts into one application.
  • useState can manage student records and form data.
  • Controlled inputs keep form values synchronized with React state.
  • Props allow data and functions to move between components.
  • map() can render a list of students.
  • filter() can implement searching and deleting.
  • Conditional rendering can display empty-state messages.
  • Reusable components make the project easier to maintain.
  • Callback props allow child components to communicate actions to parents.
  • A real application can connect the React frontend to an API and database.

🧠 Quick Quiz

Question: Which React feature is commonly used to store and update the list of students in this mini project?