Skip to main content
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

Contact.jsx
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:
.env
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 grid
grid-cols-1 lg:grid-cols-2
Contact info on left, form on right

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

1

Create Account

Sign up at emailjs.com
2

Add Email Service

Connect your email provider (Gmail, Outlook, etc.)
3

Create Template

Design your email template with variables like {{from_name}}, {{message}}
4

Get Credentials

Copy Service ID, Template ID, and Public Key to your .env file
5

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

Build docs developers (and LLMs) love