In this post, I will share with you a React JS project which is a simple CRUD application.
The React JS CRUD application looks like the following image, and if you want to see a demo of the application, click on the Live Demo button.

React JS CRUD application
This is a very simple and single-page application, here we will not make any API requests, we will simply save the data to the browser’s local storage.
Application folder structure
I installed my react development env through Vite. But, if you have installed react env via create-react-app
then my main.jsx
will be your index.js
.

import React from 'react'
import ReactDOM from 'react-dom/client'
import App from './App'
import './index.css'
ReactDOM.createRoot(document.getElementById('root')).render(
<React.StrictMode>
<App />
</React.StrictMode>
)
import { useState, useEffect } from "react";
import "./App.css";
function App() {
const [newUser, setNewUser] = useState({ name: "", email: "" });
const [data, setData] = useState([]);
const [edit, setEdit] = useState({
isEdit: false,
id: null,
});
useEffect(function(){
const datax = localStorage.getItem('users')
if (datax) setData(JSON.parse(datax));
},[]);
const saveToBrowser = (data) => {
localStorage.setItem('users', JSON.stringify(data));
}
const onInputChange = (e, field) => {
setNewUser({
...newUser,
[field]: e.target.value,
});
};
const areEmptyFields = () => {
if (newUser.name.trim() == "" || newUser.email.trim() == "") {
alert("Please fill all the required fields!");
return true;
}
return false;
};
const formSubmit = (e) => {
e.preventDefault();
if (areEmptyFields()) return;
if (edit.isEdit && edit.id) {
for (const obj of data) {
if (obj.id === edit.id) {
obj.name = newUser.name;
obj.email = newUser.email;
break;
}
}
setEdit({ isEdit: false, id: null });
setNewUser({ name: "", email: "" });
saveToBrowser(data);
return;
}
data.unshift({
id: Date.now(),
...newUser,
});
setNewUser({ name: "", email: "" });
saveToBrowser(data);
e.target.reset();
};
const editUser = (id) => {
const theUser = data.filter((usr) => usr.id === id);
if (theUser[0]) {
setNewUser({ name: theUser[0].name, email: theUser[0].email });
setEdit({
...edit,
isEdit: true,
id,
});
}
};
const cancelEdit = () => {
setEdit({ isEdit: false, id: null });
setNewUser({ name: "", email: "" });
};
const delUser = (id) => {
if (confirm("Are you sure?")) {
let users = data.filter((usr) => usr.id !== id);
setData(users);
saveToBrowser(users);
}
};
return (
<div className="container">
<h1>CRUD APP</h1>
<form onSubmit={formSubmit}>
<label htmlFor="userName">Name:</label>
<input
type="text"
name="uname"
id="userName"
placeholder="Name"
autoComplete="off"
value={newUser.name}
onChange={(e) => onInputChange(e, "name")}
/>
<label htmlFor="userEmail">Email:</label>
<input
type="email"
name="uemail"
id="userEmail"
placeholder="Email"
autoComplete="off"
value={newUser.email}
onChange={(e) => onInputChange(e, "email")}
/>
{edit.isEdit && edit.id ? (
<div className="edit-btn-wrap">
<button className="updateBtn" type="submit">
Update
</button>
<span className="cancelEdit" onClick={cancelEdit}>
Cancel
</span>
</div>
) : (
<button className="submit" type="submit">
Add User
</button>
)}
</form>
<div className="users">
<ul>
{data.map((item) => {
return (
<li className="profile" key={item.id}>
<span className="info">
<span className="image">
<img
src={`https://robohash.org/${item.id}?size=100x100&bgset=bg1`}
alt={item.name}
/>
</span>
<span className="name">
{item.name}
<br />
{item.email}
</span>
</span>
<span className="action">
<button id="editBtn" onClick={() => editUser(item.id)}>
Edit
</button>
<button id="delBtn" onClick={() => delUser(item.id)}>
Delete
</button>
</span>
</li>
);
})}
</ul>
</div>
</div>
);
}
export default App;
@import url("https://fonts.googleapis.com/css2?family=Open+Sans:wght@400;700&display=swap");
*,
*::after,
*::before {
box-sizing: border-box;
}
:root {
font-family: "Open Sans", sans-serif;
font-size: 16px;
line-height: 1.5em;
font-weight: 400;
color: #222222;
--black-color: #202124;
background-color: #f7f7f7;
font-synthesis: none;
text-rendering: optimizeLegibility;
-webkit-font-smoothing: antialiased;
-moz-osx-font-smoothing: grayscale;
-webkit-text-size-adjust: 100%;
}
.container {
max-width: 600px;
margin: 20px auto;
padding: 20px;
background-color: white;
border: 5px solid var(--black-color);
}
h1 {
text-align: center;
text-decoration: underline wavy;
}
.container form {
padding: 10px 20px 20px;
display: flex;
flex-flow: column wrap;
}
form label {
font-size: 18px;
font-weight: bold;
margin-top: 15px;
}
form label:first-child {
margin-top: 0;
}
input,
button {
all: unset;
font-size: 1rem;
}
input[type="text"],
input[type="email"] {
padding: 15px;
border: 2px solid var(--black-color);
}
.updateBtn,
.cancelEdit,
.submit {
margin-top: 20px;
cursor: pointer;
background: var(--black-color);
color: white;
padding: 15px;
text-align: center;
font-weight: bold;
}
.edit-btn-wrap{
display: flex;
align-items: center;
gap: 20px;
margin-top: 20px;
}
.cancelEdit,
.updateBtn{
flex: 1;
margin: 0;
}
.cancelEdit{
background: #f1f1f1;
color: #222222;
}
ul {
list-style-type: none;
padding: 0 20px;
}
.profile {
display: flex;
flex-wrap: wrap;
align-items: center;
justify-content: space-between;
margin-bottom: 20px;
}
.profile .info {
display: flex;
flex-wrap: wrap;
align-items: center;
}
.profile .image {
border-radius: 50%;
border: 3px solid var(--black-color);
width: 50px;
height: 50px;
overflow: hidden;
margin-right: 10px;
}
.profile img {
width: 100%;
height: 100%;
transform: scale(1.5);
}
.profile .name {
font-size: 14px;
color: #555;
}
.profile .name::first-line {
font-weight: bold;
font-size: 20px;
color: var(--black-color);
}
.profile .action {
text-align: right;
margin-right: -3px;
}
.profile .action button {
border: 1px solid var(--black-color);
margin: 3px;
padding: 3px 7px;
cursor: pointer;
background-color: #f1f1f1;
}
#delBtn {
border: 0;
color: #555555;
}
@media (max-width: 550px) {
.profile {
background-color: #f7f7f7;
padding: 20px 10px;
}
.profile,
.profile .info {
justify-content: center;
flex-direction: column;
text-align: center;
}
.profile .action button {
font-size: 14px;
}
}