📌 Introduction:
Want to build a full contact form that actually works?
In this tutorial, I’ll show you how to build a fully functional Contact Form using React for the frontend, Node.js + Express for the backend, and MongoDB to store the data.
Plus, we’ll send email notifications every time someone submits the form!
This is perfect if you're:
Learning how the frontend talks to the backend
Building your portfolio
Wanting to offer real-time feedback to users
Let’s dive in! 🛠️
🧱 Project Structure
contact-form/
├── client/ # React frontend
└── server/ # Node.js + Express backend
npx create-react-app client
cd client
npm install axios
📄 Create a Simple Contact Form
client/src/ContactForm.js
import React, { useState } from 'react';
import axios from 'axios';
const ContactForm = () => {
const [form, setForm] = useState({ name: '', email: '', message: '' });
const [status, setStatus] = useState('');
const handleChange = e => {
setForm({ ...form, [e.target.name]: e.target.value });
};
const handleSubmit = async e => {
e.preventDefault();
try {
await axios.post('http://localhost:5000/api/contact', form);
setStatus('Message sent successfully!');
setForm({ name: '', email: '', message: '' });
} catch (err) {
setStatus('Failed to send message. Please try again.');
}
};
return (
{status}
export default ContactForm;
🔹 Step 2: Backend with Node.js & Express
✅ Setup
mkdir server
cd server
npm init -y
npm install express mongoose nodemailer cors dotenv
📄 Create Backend Files
server/index.js
const express = require('express');
const mongoose = require('mongoose');
const cors = require('cors');
require('dotenv').config();
const contactRoutes = require('./routes/contact');
const app = express();
app.use(cors());
app.use(express.json());
app.use('/api/contact', contactRoutes);
mongoose.connect(process.env.MONGO_URI)
.then(() => app.listen(5000, () => console.log('Server running on port 5000')))
.catch(err => console.log(err));
📄 Create Contact Route
server/routes/contact.js
const express = require('express');
const router = express.Router();
const Contact = require('../models/Contact');
const nodemailer = require('nodemailer');
router.post('/', async (req, res) => {
const { name, email, message } = req.body;
try {
const contact = new Contact({ name, email, message });
await contact.save();
// Email Notification
const transporter = nodemailer.createTransport({
service: 'gmail',
auth: {
user: process.env.EMAIL_USER,
pass: process.env.EMAIL_PASS
}
});
const mailOptions = {
from: email,
to: process.env.EMAIL_USER,
subject: 'New Contact Form Message',
text: `From: ${name}\nEmail: ${email}\nMessage: ${message}`
};
await transporter.sendMail(mailOptions);
res.status(200).json({ msg: 'Message sent' });
} catch (err) {
res.status(500).json({ msg: 'Error sending message' });
}
});
module.exports = router;
📄 MongoDB Schema
server/models/Contact.js
const mongoose = require('mongoose');
const contactSchema = new mongoose.Schema({
name: String,
email: String,
message: String,
createdAt: {
type: Date,
default: Date.now
}
});
module.exports = mongoose.model('Contact', contactSchema);
🌐 .env File
server/.env
MONGO_URI=your_mongodb_connection_string
EMAIL_USER=your_email@gmail.com
EMAIL_PASS=your_email_password
💡 Tip: Use environment variables securely. Don't commit .env to GitHub!
✅ Final Step: Test It!
Run the server:
node index.js
Run the React app:
npm start
Visit http://localhost:3000 and try sending a message!
🧠 Common Mistakes
Forgot to enable CORS in backend.
Backend isn’t running or connected to MongoDB.
Gmail blocks Nodemailer – use app password or switch to another SMTP.
🏁 Conclusion
Now you have a full-stack contact form that:
Takes input from users
Sends data to the backend
Saves the message in MongoDB
Sends you an email instantly 🚀