βοΈ React Frontend for Node.js + MongoDB CRUD App with Image Upload
In this section, we will build the React.js frontend that connects to the Node.js API we created earlier. The frontend will allow users to create, view, edit, and delete users β along with uploading and displaying profile images.
π React Project Structure
We will use Axios for API communication and React Router for navigation.
user-crud-frontend/
β package.json
β
ββββsrc
β App.js
β index.js
β api.js
β
ββββcomponents
β UserList.js
β UserForm.js
β EditUser.js
π¦ Step 1: Install React App with Vite & Dependencies
Instead of using create-react-app
, weβll use Vite β a fast and modern build tool. This also works seamlessly with React Router and Axios.
# Install Vite and create a React app
npm create vite@latest user-crud-frontend -- --template react
# Move into the project folder
cd user-crud-frontend
# Install dependencies
npm install
# Install Axios and React Router DOM
npm install axios react-router-dom
# Start the development server
npm run dev
π Step 2: Axios API File β src/api.js
import axios from 'axios';
const API = axios.create({
baseURL: 'http://localhost:5000', // Your backend URL
});
export default API;
π Step 3: React Routing β src/App.js
import React from 'react';
import { BrowserRouter as Router, Routes, Route } from 'react-router-dom';
import UserList from './components/UserList';
import UserForm from './components/UserForm';
import EditUser from './components/EditUser';
function App() {
return (
<Router>
<Routes>
<Route path="/" element={} />
<Route path="/add" element={} />
<Route path="/edit/:id" element={} />
</Routes>
</Router>
);
}
export default App;
π₯ Step 4: User List β src/components/UserList.js
import React, { useEffect, useState } from 'react';
import { Link } from 'react-router-dom';
import API from '../api';
function UserList() {
const [users, setUsers] = useState([]);
const fetchUsers = async () => {
const res = await API.get('/api/users');
setUsers(res.data);
};
const deleteUser = async (id) => {
await API.delete(`/api/users/${id}`);
fetchUsers();
};
useEffect(() => {
fetchUsers();
}, []);
return (
<div>
<h2>User List</h2>
<Link to="/add">Add New User</Link>
<ul>
{users.map(user => (
<li key={user._id}>
<img
src={`http://localhost:5000/uploads/${user.image}`}
alt="user"
width="50"
height="50"
/>
{user.first_name} {user.last_name} - {user.email}
<Link to={`/edit/${user._id}`}>Edit</Link>
<button onClick={() => deleteUser(user._id)}>Delete</button>
</li>
))}
</ul>
</div>
);
}
export default UserList;
β Step 5: Add User β src/components/UserForm.js
import React, { useState } from 'react';
import { useNavigate } from 'react-router-dom';
import API from '../api';
function UserForm() {
const [form, setForm] = useState({
first_name: '',
last_name: '',
email: '',
password: '',
image: null,
});
const navigate = useNavigate();
const handleChange = (e) => {
const { name, value, files } = e.target;
setForm({ ...form, [name]: files ? files[0] : value });
};
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
Object.entries(form).forEach(([key, value]) => formData.append(key, value));
await API.post('/api/users', formData);
navigate('/');
};
return (
<form onSubmit={handleSubmit}>
<h2>Add User</h2>
<input name="first_name" placeholder="First Name" onChange={handleChange} />
<input name="last_name" placeholder="Last Name" onChange={handleChange} />
<input name="email" placeholder="Email" onChange={handleChange} />
<input name="password" type="password" placeholder="Password" onChange={handleChange} />
<input type="file" name="image" onChange={handleChange} />
<button type="submit">Create</button>
</form>
);
}
export default UserForm;
βοΈ Step 6: Edit User β src/components/EditUser.js
import React, { useEffect, useState } from 'react';
import { useNavigate, useParams } from 'react-router-dom';
import API from '../api';
function EditUser() {
const { id } = useParams();
const [form, setForm] = useState({
first_name: '',
last_name: '',
email: '',
password: '',
image: null,
});
const navigate = useNavigate();
useEffect(() => {
API.get(`/api/users/${id}`).then(res => setForm(res.data));
}, [id]);
const handleChange = (e) => {
const { name, value, files } = e.target;
setForm({ ...form, [name]: files ? files[0] : value });
};
const handleSubmit = async (e) => {
e.preventDefault();
const formData = new FormData();
Object.entries(form).forEach(([key, value]) => {
if (value !== null) formData.append(key, value);
});
await API.put(`/api/users/${id}`, formData);
navigate('/');
};
return (
<form onSubmit={handleSubmit}>
<h2>Edit User</h2>
<input name="first_name" value={form.first_name} onChange={handleChange} />
<input name="last_name" value={form.last_name} onChange={handleChange} />
<input name="email" value={form.email} onChange={handleChange} />
<input name="password" type="password" onChange={handleChange} />
<input type="file" name="image" onChange={handleChange} />
<button type="submit">Update</button>
</form>
);
}
export default EditUser;