Skip to main content

Contact

The Contact component (GetContact) provides a fully functional contact form with email integration via EmailJS and spam protection using Google reCAPTCHA. It features a modern design with SpotlightCard effects and toast notifications.

Overview

This component creates an engaging contact section with a form that sends emails directly to your inbox without requiring a backend server. It includes client-side validation, reCAPTCHA verification, and user feedback via toast notifications.

Dependencies

npm install @emailjs/browser react-google-recaptcha
import { useRef, useState } from 'react';
import emailjs from '@emailjs/browser';
import SpotlightCard from '../SpotlightCard/SpotLightCard';
import ReCAPTCHA from 'react-google-recaptcha';

Props

The GetContact component does not accept props. Configuration is done through EmailJS service and reCAPTCHA setup.

Usage

import GetContact from './components/Getcontact/Getcontact';

function App() {
  return (
    <main>
      <section id="contact">
        <GetContact />
      </section>
    </main>
  );
}

Setup Requirements

1. EmailJS Configuration

You need to create an EmailJS account and configure a service and template:
  1. Sign up at EmailJS
  2. Create an email service (Gmail, Outlook, etc.)
  3. Create an email template
  4. Get your Service ID, Template ID, and Public Key
In the component, update these values:
emailjs.sendForm(
  'service_dl7ji3p',        // Your Service ID
  'template_pfbhj3g',       // Your Template ID
  form.current, 
  'BbaI-yQrHrGFriM_6'      // Your Public Key
)

2. reCAPTCHA Configuration

  1. Get a reCAPTCHA site key from Google reCAPTCHA
  2. Add to your environment variables:
# .env
VITE_SITE_KEY=your_recaptcha_site_key_here
Never commit your reCAPTCHA site key or EmailJS credentials to public repositories. Use environment variables.

Features

1. Form Fields

The form includes three fields:
{/* Name field */}
<input 
  type="text" 
  name="name" 
  required 
  placeholder="Your name"
/>

{/* Email field */}
<input 
  type="email" 
  name="email" 
  required 
  placeholder="Your email"
/>

{/* Message field */}
<textarea 
  name="message" 
  rows="5" 
  required 
  placeholder="Write your message here..."
/>

2. reCAPTCHA Integration

const [captchaVerified, setCaptchaVerified] = useState(false);

<ReCAPTCHA
  ref={recaptchaRef}
  sitekey={import.meta.env.VITE_SITE_KEY}
  onChange={() => setCaptchaVerified(true)}
  onExpired={() => setCaptchaVerified(false)}
  theme="dark"
/>
Prevents form submission without verification:
if (!captchaVerified) {
  showToast("Please complete the reCAPTCHA.", "error");
  return;
}

3. Toast Notifications

Provides user feedback for:
  • Successful submission
  • Errors
  • Missing reCAPTCHA verification
const [toast, setToast] = useState(null);

const showToast = (msg, type = "success") => {
  setToast({ msg, type });
  setTimeout(() => setToast(null), 3500);
};

4. Form Reset

Automatically resets form and reCAPTCHA after successful submission:
.then(() => {
  showToast("Message sent successfully!");
  form.current.reset();
  recaptchaRef.current.reset();
  setCaptchaVerified(false);
})

Component Structure

function GetContact() {
  const form = useRef();
  const recaptchaRef = useRef();
  const [toast, setToast] = useState(null);
  const [captchaVerified, setCaptchaVerified] = useState(false);

  const sendEmail = (e) => {
    e.preventDefault();
    
    if (!captchaVerified) {
      showToast("Please complete the reCAPTCHA.", "error");
      return;
    }

    emailjs.sendForm(/* ... */)
      .then(() => {
        showToast("Message sent successfully!");
        // Reset form
      })
      .catch(() => {
        showToast("Something went wrong. Try again.", "error")
      });
  };

  return (
    <section>
      {/* Content section */}
      {/* Form in SpotlightCard */}
      {/* Toast notification */}
    </section>
  );
}

Customization

Personalizing Text Content

<div className="w-full md:flex-1 text-center md:text-left md:mr-24">
  <p className="text-sm tracking-widest uppercase text-white/50 mb-2">
    [YOUR LABEL]
  </p>
  <h2 className="text-3xl font-bold text-white mb-4">
    [Your Heading]
  </h2>
  <hr className="border-2 border-[#3A3C41] mb-4 w-3/4 mx-auto md:mx-0" />
  <p className="text-[#C7C7C7] text-lg leading-relaxed">
    [Your description]
  </p>
</div>

Adding More Form Fields

<form ref={form} onSubmit={sendEmail}>
  {/* Existing fields... */}
  
  {/* NEW: Phone field */}
  <div className="flex flex-col mb-6">
    <label className="font-semibold text-white mb-2">Phone</label>
    <input 
      type="tel" 
      name="phone" 
      placeholder="Your phone number"
      className="px-4 py-3 border border-[#3A3C41] rounded-xl bg-[#1E1F22] text-white outline-none focus:border-[#4C8CF5] transition-colors duration-200"
    />
  </div>
  
  {/* NEW: Subject field */}
  <div className="flex flex-col mb-6">
    <label className="font-semibold text-white mb-2">Subject</label>
    <input 
      type="text" 
      name="subject" 
      required
      placeholder="Message subject"
      className="px-4 py-3 border border-[#3A3C41] rounded-xl bg-[#1E1F22] text-white outline-none focus:border-[#4C8CF5] transition-colors duration-200"
    />
  </div>
</form>
When adding new fields, update your EmailJS template to include the new field names.

Custom Toast Styling

{/* Success toast - Green */}
{toast && toast.type === 'success' && (
  <div className="fixed top-5 right-5 px-5 py-3 rounded-xl text-white text-sm z-50 shadow-lg bg-green-500">
    {toast.msg}
  </div>
)}

{/* Error toast - Red */}
{toast && toast.type === 'error' && (
  <div className="fixed top-5 right-5 px-5 py-3 rounded-xl text-white text-sm z-50 shadow-lg bg-red-500">
    {toast.msg}
  </div>
)}

Alternative Form Layouts

{/* Two-column layout for name and email */}
<div className="flex gap-4 mb-6">
  <div className="flex flex-col flex-1">
    <label className="font-semibold text-white mb-2">Name</label>
    <input type="text" name="name" required />
  </div>
  <div className="flex flex-col flex-1">
    <label className="font-semibold text-white mb-2">Email</label>
    <input type="email" name="email" required />
  </div>
</div>

EmailJS Template Setup

Create a template in EmailJS with these variables:
<!-- Email template example -->
<h2>New Contact Form Submission</h2>

<p><strong>Name:</strong> {{name}}</p>
<p><strong>Email:</strong> {{email}}</p>
<p><strong>Message:</strong></p>
<p>{{message}}</p>
The name attributes in your form inputs must match the template variables:
<input name="name" />      {/* matches {{name}} */}
<input name="email" />     {/* matches {{email}} */}
<textarea name="message" /> {/* matches {{message}} */}

Styling Details

Form Input Styling

className="px-4 py-3 border border-[#3A3C41] rounded-xl bg-[#1E1F22] text-white outline-none focus:border-[#4C8CF5] transition-colors duration-200"
  • Dark background (#1E1F22)
  • Gray border (#3A3C41)
  • Blue focus border (#4C8CF5)
  • Rounded corners (12px)
  • Smooth color transitions

Submit Button

className="w-full bg-white/10 backdrop-blur-md text-white px-5 py-3 rounded-xl text-sm font-medium border border-white/20 hover:bg-white/20 hover:-translate-y-0.5 active:translate-y-0 transition-all duration-200 shadow-lg"
  • Full width
  • Glassmorphic effect
  • Hover lift animation
  • Active press animation

Spotlight Card

spotlightColor="rgba(0, 229, 255, 0.2)"
className="w-full max-w-[450px] rounded-2xl"
  • Cyan spotlight effect
  • Max width 450px
  • Rounded corners

Responsive Design

Layout

  • Mobile (< 768px):
    • Single column layout
    • Centered alignment
    • Smaller padding
  • Desktop (≥ 768px):
    • Two-column layout (text + form)
    • Left-aligned text
    • Larger padding (p-8)

Form Sizing

className="w-full max-w-[450px]"
Form has:
  • Full width on mobile
  • Max 450px width on larger screens
  • Centered in container

Error Handling

emailjs.sendForm(/* ... */)
  .then(() => {
    showToast("Message sent successfully!");
    form.current.reset();
    recaptchaRef.current.reset();
    setCaptchaVerified(false);
  })
  .catch((error) => {
    console.error('EmailJS Error:', error);
    showToast("Something went wrong. Try again.", "error");
  });

Accessibility

  • Semantic HTML form elements
  • Proper label associations
  • Required field validation
  • Focus states on inputs
  • ARIA-friendly reCAPTCHA
  • Error messages via toast
  • Keyboard accessible

Security Best Practices

Follow these security best practices:
  1. Environment Variables: Store sensitive keys in .env file
  2. reCAPTCHA: Always validate reCAPTCHA before submission
  3. Email Validation: Use type="email" for email fields
  4. Rate Limiting: EmailJS provides automatic rate limiting
  5. Domain Restrictions: Configure allowed domains in EmailJS dashboard

Common Issues & Solutions

EmailJS Not Working

// Make sure form fields have name attributes
<input name="name" /> // ✓ Correct
<input />             // ✗ Wrong

// Verify EmailJS credentials are correct
emailjs.sendForm(
  'YOUR_SERVICE_ID',  // Check this
  'YOUR_TEMPLATE_ID', // Check this
  form.current,
  'YOUR_PUBLIC_KEY'   // Check this
)

reCAPTCHA Not Showing

// Ensure site key is loaded from environment
const siteKey = import.meta.env.VITE_SITE_KEY; // Vite
const siteKey = process.env.REACT_APP_SITE_KEY; // Create React App

<ReCAPTCHA
  sitekey={siteKey}
  // ...
/>

Toast Not Disappearing

// Ensure timeout is properly set
const showToast = (msg, type = "success") => {
  setToast({ msg, type });
  setTimeout(() => setToast(null), 3500); // 3.5 seconds
};

Testing

  1. Test form submission with valid data
  2. Test validation by submitting empty fields
  3. Test reCAPTCHA by submitting without verification
  4. Test error handling by using invalid EmailJS credentials
  5. Test responsiveness on different screen sizes
  6. Check email delivery in your inbox
EmailJS has a free tier limit of 200 emails per month. Monitor your usage in the EmailJS dashboard.