Skip to content

Conversation

@omkadu8767
Copy link

@omkadu8767 omkadu8767 commented Sep 8, 2025

Related Issue


Description

This PR integrates EmailJS into the Contact page, enabling users to send messages directly from the contact form to the project team via email. The form fields are now connected to EmailJS, and upon submission, the data is sent using the configured EmailJS service, template, and public key. The UI and user experience remain unchanged, except for the new email functionality.


How Has This Been Tested?

  • Manually tested the contact form by submitting various messages.
  • Verified that emails are received as expected.
  • Confirmed that the success popup appears after successful submission.
  • Checked that the form resets after sending and handles loading state correctly.

Type of Change

  • Bug fix
  • New feature
  • Code style update
  • Breaking change
  • Documentation update

EmailJS Setup Instructions

  1. Create an EmailJS Account

  2. Add an Email Service

    • In the EmailJS dashboard, go to the "Email Services" section.
    • Click "Add new service" and connect your preferred email provider (e.g., Gmail, Outlook).
  3. Create an Email Template

    • Go to the "Email Templates" section.
    • Click "Create New Template".
    • Add variables for the fields you want to send (e.g., from_name, from_email, subject, message).
    • Save the template and note the Template ID.
  4. Get Your Service ID and Public Key

    • Go to the "Integration" section.
    • Copy your Service ID and Public Key.
  5. Configure Environment Variables

    • In your project root, create a .env file (or .env.local for Vite/React).
    • Add the following variables:
      VITE_EMAILJS_SERVICE_ID=your_service_id
      VITE_EMAILJS_TEMPLATE_ID=your_template_id
      VITE_EMAILJS_PUBLIC_KEY=your_public_key
      
    • Replace the placeholders with your actual EmailJS credentials.
  6. Update the Code to Use Environment Variables

    • In your code, access the variables using import.meta.env.VITE_EMAILJS_SERVICE_ID, etc.

    Example:

    const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
    const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID;
    const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY;
  7. Restart the Development Server
    After updating the .env file, restart your dev server to apply the changes.

Note:

Never commit your .env file to version control. Add .env to your .gitignore.
For more details, see the EmailJS documentation.

Summary by CodeRabbit

  • New Features
    • Contact form now sends messages directly via email.
    • Displays a success confirmation after sending.
    • Prevents accidental page reloads on submit for a smoother experience.
  • Refactor
    • Improved form handling under the hood for better reliability.
  • Chores
    • Updated dependencies to support email sending functionality.

@netlify
Copy link

netlify bot commented Sep 8, 2025

Deploy Preview for github-spy ready!

Name Link
🔨 Latest commit 4e2bf7b
🔍 Latest deploy log https://app.netlify.com/projects/github-spy/deploys/68be9d08ef7da50008f1ec9e
😎 Deploy Preview https://deploy-preview-208--github-spy.netlify.app
📱 Preview on mobile
Toggle QR Code...

QR Code

Use your smartphone camera to open QR code link.

To edit notification comments on pull requests, go to your Netlify project configuration.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Sep 8, 2025

Walkthrough

Added EmailJS dependency and integrated EmailJS sending in Contact form. Replaced simulated submission with emailjs.send using env-driven IDs. Implemented refs for inputs, wired refs to fields, handled success by showing popup and clearing fields, added basic error handling internally. No changes to exports or component signature.

Changes

Cohort / File(s) Summary
Dependencies
package.json
Added dependency @emailjs/browser at version ^4.4.1.
Contact form integration
src/pages/Contact/Contact.tsx
Integrated EmailJS via emailjs.send with env-based serviceId, templateId, publicKey. Replaced simulated timeout. Switched to refs (nameRef, emailRef, subjectRef, messageRef) for reading values. Extended imports (emailjs, useRef, CheckCircle). Updated submit handler to prevent default, send email, show success UI, clear fields, and reset submitting state. Minor className refactors.

Sequence Diagram(s)

sequenceDiagram
  autonumber
  actor U as User
  participant CF as ContactForm (React)
  participant EJ as EmailJS SDK
  participant ES as EmailJS Service

  U->>CF: Click "Send"
  CF->>CF: Read values via refs
  alt Has env IDs
    CF->>EJ: emailjs.send(serviceId, templateId, templateParams, publicKey)
    EJ->>ES: Deliver request
    ES-->>EJ: Response (success/failure)
    alt Success
      EJ-->>CF: Resolve
      CF->>CF: Show success popup<br/>Clear fields<br/>Set submitting=false
    else Failure
      EJ-->>CF: Reject
      CF->>CF: Log/handle error internally<br/>Set submitting=false
    end
  else Missing config
    CF->>CF: Skip send / handle error
  end
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Assessment against linked issues

Objective Addressed Explanation
Integrate EmailJS send; replace simulated submission (#203)
Use controlled components with useState for all fields (#203) Inputs use refs; no per-field state present.
Validate all fields; do not send data unless validation passes (#203) No explicit validation before sending.
Show success and error notifications on outcome (#203) Success popup shown; error handling not user-facing.
Avoid exposing sensitive information (use env) (#203)

Assessment against linked issues: Out-of-scope changes

Code Change Explanation
Minor className/style refactors in Contact form (src/pages/Contact/Contact.tsx) These stylistic adjustments are not required by the EmailJS integration objectives; likely incidental, but do not impact functionality.

Poem

I thump my paws—send mail, hooray!
From fields to clouds, it’s flown today.
A carrot-click, a gentle cheer,
Success pops up—hip hop, my dear!
Next hop: guard errors, validate—
A tidy burrow’s best, don’t wait. 🥕✉️

✨ Finishing Touches
  • 📝 Generate Docstrings
🧪 Generate unit tests
  • Create PR with unit tests
  • Post copyable unit tests in a comment

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@github-actions github-actions bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 4

🧹 Nitpick comments (3)
src/pages/Contact/Contact.tsx (3)

36-41: Trim inputs before sending.

Prevents accidental leading/trailing spaces from being included in the email.

Apply this diff:

-    const templateParams = {
-      from_name: nameRef.current?.value || "",
-      from_email: emailRef.current?.value || "",
-      subject: subjectRef.current?.value || "",
-      message: messageRef.current?.value || "",
-    };
+    const templateParams = {
+      from_name: (nameRef.current?.value || "").trim(),
+      from_email: (emailRef.current?.value || "").trim(),
+      subject: (subjectRef.current?.value || "").trim(),
+      message: (messageRef.current?.value || "").trim(),
+    };

55-59: Tie auto-close to popup visibility and clean up timer on unmount.

Create the timeout when showPopup flips true; clear it on unmount to avoid stray state updates.

Apply this diff to remove the unconditional timeout:

-    // Auto-close popup after 5 seconds
-    setTimeout(() => {
-      setShowPopup(false);
-    }, 5000);

Add this effect near other hooks:

// Auto-close success popup after 5s
useEffect(() => {
  if (!showPopup) return;
  const id = setTimeout(() => setShowPopup(false), 5000);
  return () => clearTimeout(id);
}, [showPopup]);

Also applies to: 336-356


17-18: Null-safe ThemeContext access (optional).

If ThemeContext is ever null outside a provider, the cast could throw at runtime. Guard or provide a default.

Example:

const theme = useContext(ThemeContext);
const mode = theme?.mode ?? "light";
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 22e52f9 and 4e2bf7b.

📒 Files selected for processing (2)
  • package.json (1 hunks)
  • src/pages/Contact/Contact.tsx (10 hunks)
🧰 Additional context used
🧬 Code graph analysis (1)
src/pages/Contact/Contact.tsx (1)
src/context/ThemeContext.tsx (2)
  • ThemeContext (10-10)
  • ThemeContextType (49-49)
🔇 Additional comments (1)
package.json (1)

15-15: EmailJS browser dependency addition looks good.

Adding "@emailjs/browser" as a runtime dependency is appropriate for client-side EmailJS usage.

Comment on lines +20 to +25
// Refs for form fields
const nameRef = useRef<HTMLInputElement>(null);
const emailRef = useRef<HTMLInputElement>(null);
const subjectRef = useRef<HTMLSelectElement>(null);
const messageRef = useRef<HTMLTextAreaElement>(null);

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Inputs are uncontrolled; Issue #203 requests controlled components.

The linked issue explicitly calls for useState-managed, controlled inputs with validation. Consider switching refs to state for consistency and easier validation UX.

Example (sketch, not a full diff):

// state
const [name, setName] = useState("");
const [email, setEmail] = useState("");
const [subject, setSubject] = useState("");
const [message, setMessage] = useState("");

// inputs
<input value={name} onChange={(e) => setName(e.target.value)} ... />
<input type="email" value={email} onChange={(e) => setEmail(e.target.value)} ... />
<select value={subject} onChange={(e) => setSubject(e.target.value)} required>...</select>
<textarea value={message} onChange={(e) => setMessage(e.target.value)} ... />

// template params
const templateParams = { from_name: name.trim(), from_email: email.trim(), subject: subject.trim(), message: message.trim() };

// clear after success
setName(""); setEmail(""); setSubject(""); setMessage("");

If you want, I can generate a complete patch converting all four fields.

Also applies to: 234-242, 259-263, 278-285, 311-315, 36-41, 47-51

🤖 Prompt for AI Agents
In src/pages/Contact/Contact.tsx around lines 20-25 (also apply to 36-41, 47-51,
234-242, 259-263, 278-285, 311-315): replace the uncontrolled refs for name,
email, subject and message with useState-managed controlled inputs; create state
variables and setters for each field, bind each input/select/textarea value to
its state and add onChange handlers to update state, enforce validation
(required and email format) on inputs, when building templateParams use trimmed
state values, and after a successful submit reset all state fields to empty
strings; remove the refs and any direct DOM reads and ensure any existing logic
that cleared or referenced refs is updated to use the new state variables.

Comment on lines +27 to 29
const handleSubmit = async (e?: React.MouseEvent<HTMLButtonElement>) => {
if (e) e.preventDefault();
setIsSubmitting(true);
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Submit via form onSubmit to leverage native required-field validation.

Right now required attributes won’t prevent the button click. Hook the handler to the form’s onSubmit and make the button type="submit".

Apply this diff:

-  const handleSubmit = async (e?: React.MouseEvent<HTMLButtonElement>) => {
-    if (e) e.preventDefault();
+  const handleSubmit = async (e: React.FormEvent<HTMLFormElement>) => {
+    e.preventDefault();
-            <div className="space-y-3 sm:space-y-4 flex-1 flex flex-col">
+            <form onSubmit={handleSubmit} className="space-y-3 sm:space-y-4 flex-1 flex flex-col" noValidate>
-                  <button
-                    onClick={handleSubmit}
-                    disabled={isSubmitting}
+                  <button
+                    type="submit"
+                    disabled={isSubmitting}

Also applies to: 221-221, 318-324

Comment on lines +31 to 35
// EmailJS config (replace with your own)
const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID
const publicKey = import.meta.env.VITE_EMAILJS_USER_ID;

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Verification agent

🧩 Analysis chain

Fix env var name for EmailJS public key and guard missing config.

Use VITE_EMAILJS_PUBLIC_KEY (per your PR docs) instead of VITE_EMAILJS_USER_ID. Also bail out early if any config is missing to avoid silent failures.

Apply this diff:

-    const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
-    const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID
-    const publicKey = import.meta.env.VITE_EMAILJS_USER_ID;
+    const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
+    const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID;
+    const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY;
+
+    if (!serviceId || !templateId || !publicKey) {
+      // Surface a clear signal in dev and prevent the send call
+      console.error("Missing EmailJS configuration (serviceId/templateId/publicKey).");
+      setIsSubmitting(false);
+      return;
+    }

Run to find inconsistent usages across the repo (and docs):


🏁 Script executed:

#!/bin/bash
rg -nP 'VITE_EMAILJS_(SERVICE_ID|TEMPLATE_ID|PUBLIC_KEY|USER_ID)'

Length of output: 285


Fix EmailJS env var and guard missing config

Replace the publicKey assignment to use VITE_EMAILJS_PUBLIC_KEY and bail out early if any of the three vars is unset to prevent silent failures:

 src/pages/Contact/Contact.tsx
@@ -31,3 +31,9 @@
     const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
     const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID;
-    const publicKey  = import.meta.env.VITE_EMAILJS_USER_ID;
+    const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY;
+
+    if (!serviceId || !templateId || !publicKey) {
+      console.error("Missing EmailJS configuration (serviceId/templateId/publicKey).");
+      setIsSubmitting(false);
+      return;
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
// EmailJS config (replace with your own)
const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID
const publicKey = import.meta.env.VITE_EMAILJS_USER_ID;
// EmailJS config (replace with your own)
const serviceId = import.meta.env.VITE_EMAILJS_SERVICE_ID;
const templateId = import.meta.env.VITE_EMAILJS_TEMPLATE_ID;
const publicKey = import.meta.env.VITE_EMAILJS_PUBLIC_KEY;
if (!serviceId || !templateId || !publicKey) {
console.error("Missing EmailJS configuration (serviceId/templateId/publicKey).");
setIsSubmitting(false);
return;
}
🤖 Prompt for AI Agents
In src/pages/Contact/Contact.tsx around lines 31 to 35, the publicKey is
assigned from the wrong env var and there is no guard for missing EmailJS
config; change the publicKey assignment to use
import.meta.env.VITE_EMAILJS_PUBLIC_KEY and add an early exit when any of
serviceId, templateId or publicKey is falsy (e.g., log an error and return from
the component or disable form submission) to prevent silent failures when env
vars are not set.

Comment on lines +43 to 55
try {
await emailjs.send(serviceId, templateId, templateParams, publicKey);
setShowPopup(true);
// Optionally clear fields
if (nameRef.current) nameRef.current.value = "";
if (emailRef.current) emailRef.current.value = "";
if (subjectRef.current) subjectRef.current.value = "";
if (messageRef.current) messageRef.current.value = "";
} catch (error) {
// Optionally handle error (show error popup, etc.)
}
setIsSubmitting(false);
// Auto-close popup after 5 seconds
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🛠️ Refactor suggestion

Show user-facing errors and always reset submitting state in finally.

Errors are swallowed; users won’t know a send failed. Also prefer finally for resetting the loading flag.

Apply this diff:

-    try {
-      await emailjs.send(serviceId, templateId, templateParams, publicKey);
-      setShowPopup(true);
-      // Optionally clear fields
-      if (nameRef.current) nameRef.current.value = "";
-      if (emailRef.current) emailRef.current.value = "";
-      if (subjectRef.current) subjectRef.current.value = "";
-      if (messageRef.current) messageRef.current.value = "";
-    } catch (error) {
-      // Optionally handle error (show error popup, etc.)
-    }
-    setIsSubmitting(false);
+    try {
+      await emailjs.send(serviceId, templateId, templateParams, publicKey);
+      setShowPopup(true);
+      // Optionally clear fields
+      if (nameRef.current) nameRef.current.value = "";
+      if (emailRef.current) emailRef.current.value = "";
+      if (subjectRef.current) subjectRef.current.value = "";
+      if (messageRef.current) messageRef.current.value = "";
+    } catch (error) {
+      // Provide a user-visible notification
+      // (see import addition below)
+      toast.error("Failed to send your message. Please try again.");
+      console.error("EmailJS send failed:", error);
+    } finally {
+      setIsSubmitting(false);
+    }

Add this import (top of file):

+import { toast } from "react-hot-toast";
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
try {
await emailjs.send(serviceId, templateId, templateParams, publicKey);
setShowPopup(true);
// Optionally clear fields
if (nameRef.current) nameRef.current.value = "";
if (emailRef.current) emailRef.current.value = "";
if (subjectRef.current) subjectRef.current.value = "";
if (messageRef.current) messageRef.current.value = "";
} catch (error) {
// Optionally handle error (show error popup, etc.)
}
setIsSubmitting(false);
// Auto-close popup after 5 seconds
// At the top of src/pages/Contact/Contact.tsx
import { toast } from "react-hot-toast";
// …
// In your submit handler, replace lines 43–55 with:
try {
await emailjs.send(serviceId, templateId, templateParams, publicKey);
setShowPopup(true);
// Optionally clear fields
if (nameRef.current) nameRef.current.value = "";
if (emailRef.current) emailRef.current.value = "";
if (subjectRef.current) subjectRef.current.value = "";
if (messageRef.current) messageRef.current.value = "";
} catch (error) {
// Provide a user-visible notification
toast.error("Failed to send your message. Please try again.");
console.error("EmailJS send failed:", error);
} finally {
setIsSubmitting(false);
}
// Auto-close popup after 5 seconds
🤖 Prompt for AI Agents
In src/pages/Contact/Contact.tsx around lines 43 to 55, the send-email error is
currently swallowed and setIsSubmitting(false) is called outside a finally
block; update the try/catch to show a user-facing error (for example call
toast.error or set an error state and avoid showing the success popup) inside
the catch, and move setIsSubmitting(false) into a finally block so it always
runs; also add the required import at the top of the file (e.g., import { toast
} from 'react-toastify') and ensure the success popup is only shown on
successful send.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

🚀 Feature: Integrate Contact Form with EmailJS to Send User Messages

2 participants