The Contact component provides a comprehensive contact section with a working form powered by EmailJS, contact information cards, and decorative animated backgrounds.
Overview
Key features:
- Functional contact form with EmailJS integration
- Form validation and loading states
- Contact information cards (email, location, WhatsApp)
- Decorative animated blob backgrounds
- Two-column responsive layout
- Dark mode support
Component Structure
import React, { useState, useRef } from "react";
import { motion } from "framer-motion";
import emailjs from "@emailjs/browser";
import { styles } from "@/styles";
import { SectionWrapper } from "@/hooks";
import { fadeIn, textVariant } from "@/utils/motion";
import { Email } from "@/constants";
const Contact = () => {
const formRef = useRef();
const [form, setForm] = useState({
name: "",
email: "",
message: "",
});
const [loading, setLoading] = useState(false);
const SERVICE_ID = import.meta.env.VITE_SEVICE_ID;
const TEMPLATE_ID = import.meta.env.VITE_TEMPLATE_ID;
const PUBLIC_KEY = import.meta.env.VITE_PUBLIC_KEY;
return (
<div className="relative px-4 sm:px-6 lg:px-8 py-16 overflow-hidden">
{/* Animated backgrounds */}
{/* Section heading */}
{/* Two-column layout */}
</div>
);
};
export default SectionWrapper(Contact, "contact");
Form State Management
const [form, setForm] = useState({
name: "",
email: "",
message: "",
});
const [loading, setLoading] = useState(false);
const handleChange = (e) => {
const { name, value } = e.target;
setForm({ ...form, [name]: value });
};
EmailJS Integration
Environment Variables Setup
Create a .env file:
VITE_SEVICE_ID=your_service_id
VITE_TEMPLATE_ID=your_template_id
VITE_PUBLIC_KEY=your_public_key
Never commit your .env file to version control. Add it to .gitignore.
Form Submission Handler
const handleSubmit = (e) => {
e.preventDefault();
setLoading(true);
emailjs
.send(
SERVICE_ID,
TEMPLATE_ID,
{
from_name: form.name,
to_name: "Ritam Saha",
from_email: form.email,
to_email: Email,
message: form.message,
},
PUBLIC_KEY
)
.then(() => {
setLoading(false);
alert("Thank you. I will get back to you as soon as possible.");
setForm({
name: "",
email: "",
message: "",
});
})
.catch((error) => {
setLoading(false);
console.log(error);
alert("Something went wrong. Please try again.");
});
};
EmailJS allows you to send emails directly from client-side JavaScript without a backend server.
Contact Information Section
<motion.div
variants={fadeIn("right", "", 0.2, 1)}
className="flex flex-col space-y-6"
>
<h3 className="text-2xl font-bold text-navy-900 dark:text-slate-100">
Let's Connect
</h3>
<p className="text-navy-700 dark:text-slate-300 text-lg leading-relaxed">
I'm currently available for freelance work or full-time positions.
Drop me a line if you'd like to work together!
</p>
<div className="flex flex-col space-y-4">
{/* Email card */}
<div className="flex items-center space-x-4">
<div className="w-12 h-12 rounded-full
bg-navy-100 dark:bg-slate-700
flex items-center justify-center
border border-navy-300 dark:border-slate-600">
{/* Email icon SVG */}
</div>
<div>
<h4 className="text-navy-900 dark:text-slate-100 font-medium">
Email
</h4>
<p className="text-navy-600 dark:text-slate-300">{Email}</p>
</div>
</div>
{/* Location card */}
<div className="flex items-center space-x-4">
<div className="w-12 h-12 rounded-full
bg-navy-100 dark:bg-slate-700
flex items-center justify-center
border border-navy-300 dark:border-slate-600">
{/* Location icon SVG */}
</div>
<div>
<h4 className="text-navy-900 dark:text-slate-100 font-medium">
Location
</h4>
<p className="text-navy-600 dark:text-slate-300">
Kolkata, West Bengal, India
</p>
</div>
</div>
{/* WhatsApp card */}
<div className="flex items-center space-x-4">
<div className="w-12 h-12 rounded-full
bg-navy-100 dark:bg-slate-700
flex items-center justify-center
border border-navy-300 dark:border-slate-600">
{/* WhatsApp icon SVG */}
</div>
<div>
<h4 className="text-navy-900 dark:text-slate-100 font-medium">
Prefer direct messaging?
</h4>
<a href="https://wa.me/917044037047"
target="_blank"
rel="noopener noreferrer"
className="text-navy-700 dark:text-slate-300
hover:text-green-500 dark:hover:text-green-400">
Message me on WhatsApp
</a>
</div>
</div>
</div>
</motion.div>
Contact Form
<motion.div
variants={fadeIn("left", "", 0.2, 1)}
className="bg-white/95 dark:bg-zinc-900/10 p-8 rounded-2xl
backdrop-blur-lg shadow-xl border border-navy-200
dark:border-zinc-900"
>
<form ref={formRef} onSubmit={handleSubmit} className="flex flex-col gap-8">
{/* Name field */}
<label className="flex flex-col">
<span className="text-navy-900 dark:text-slate-100
font-medium mb-4">
Your Name
</span>
<input
type="text"
name="name"
value={form.name}
onChange={handleChange}
placeholder="What's your name?"
className="bg-navy-50 dark:bg-zinc-600/10 py-4 px-6
placeholder:text-navy-400 dark:placeholder:text-slate-400
text-navy-900 dark:text-slate-100 rounded-lg
outline-none border border-navy-200 dark:border-none
font-medium transition-all duration-300
focus:ring-2 focus:ring-navy-500 dark:focus:ring-blue-500"
required
/>
</label>
{/* Email field */}
<label className="flex flex-col">
<span className="text-navy-900 dark:text-slate-100
font-medium mb-4">
Your Email
</span>
<input
type="email"
name="email"
value={form.email}
onChange={handleChange}
placeholder="What's your email?"
className="bg-navy-50 dark:bg-zinc-600/10 py-4 px-6
placeholder:text-navy-400 dark:placeholder:text-slate-400
text-navy-900 dark:text-slate-100 rounded-lg
outline-none border border-navy-200 dark:border-none
font-medium transition-all duration-300
focus:ring-2 focus:ring-navy-500 dark:focus:ring-blue-500"
required
/>
</label>
{/* Message field */}
<label className="flex flex-col">
<span className="text-navy-900 dark:text-slate-100
font-medium mb-4">
Your Message
</span>
<textarea
rows="7"
name="message"
value={form.message}
onChange={handleChange}
placeholder="What do you want to say?"
className="bg-navy-50 dark:bg-zinc-600/10 py-4 px-6
placeholder:text-navy-400 dark:placeholder:text-slate-400
text-navy-900 dark:text-slate-100 rounded-lg
outline-none border border-navy-200 dark:border-none
font-medium resize-none transition-all duration-300
focus:ring-2 focus:ring-navy-500 dark:focus:ring-blue-500"
required
/>
</label>
{/* Submit button */}
<button
type="submit"
className="bg-navy-600 dark:bg-zinc-800
hover:bg-zinc-700 dark:hover:bg-zinc-700
py-3 px-8 outline-none w-fit text-white
font-bold shadow-lg rounded-xl
transition-all duration-300 transform hover:scale-105
flex items-center gap-2"
disabled={loading}
>
{loading && (
<span className="flex items-center">
<span className="w-5 h-5 border-t-2 border-white
rounded-full animate-spin mr-2" />
</span>
)}
{loading ? "Sending..." : "Send Message"}
</button>
</form>
</motion.div>
All form fields are required using the HTML5 required attribute.
Animated Background Blobs
<div className="absolute top-0 left-0 w-72 h-72
bg-navy-200/30 dark:bg-slate-700/20 rounded-full
mix-blend-multiply dark:mix-blend-lighten
filter blur-xl opacity-40 animate-blob" />
<div className="absolute top-0 right-0 w-72 h-72
bg-blue-200/30 dark:bg-slate-600/20 rounded-full
mix-blend-multiply dark:mix-blend-lighten
filter blur-xl opacity-40 animate-blob animation-delay-2000" />
<div className="absolute -bottom-8 left-20 w-72 h-72
bg-slate-200/30 dark:bg-slate-700/20 rounded-full
mix-blend-multiply dark:mix-blend-lighten
filter blur-xl opacity-40 animate-blob animation-delay-4000" />
Responsive Layout
Two-column gridgrid-cols-1 lg:grid-cols-2
Contact info on left, form on right Single column stack
Contact info above form
Loading State
Button changes when submitting:
<button disabled={loading}>
{loading && (
<span className="w-5 h-5 border-t-2 border-white
rounded-full animate-spin mr-2" />
)}
{loading ? "Sending..." : "Send Message"}
</button>
Setting Up EmailJS
Add Email Service
Connect your email provider (Gmail, Outlook, etc.)
Create Template
Design your email template with variables like {{from_name}}, {{message}}
Get Credentials
Copy Service ID, Template ID, and Public Key to your .env file
Install Package
npm install @emailjs/browser
Form Validation
HTML5 validation:
type="email" validates email format
required attribute ensures fields are filled
- Custom error messages via browser defaults
WhatsApp Integration
Direct messaging link:
<a href="https://wa.me/917044037047"
target="_blank"
rel="noopener noreferrer">
Message me on WhatsApp
</a>
Format: https://wa.me/[country code][phone number]
Dependencies
@emailjs/browser - Email sending service
framer-motion - Animations
@/constants - Email address constant