0

I'm trying to use the SendGrid module in my React application to send an email using a contact form. However, when I run npm start, I encounter the following error:

Failed to compile.
Module not found: Error: Can't resolve 'fs' in '.../node_modules/@sendgrid/helpers/classes'

The error message suggests that the 'fs' and 'path' modules cannot be resolved in the @sendgrid/helpers/classes/attachment.js file.

I also receive the warning:

BREAKING CHANGE: webpack < 5 used to include polyfills for node.js core modules by default.
This is no longer the case. Verify if you need this module and configure a polyfill for it.

Here is my project structure: card.tsx (React component for the contact form):

import React, { useState, useEffect, useRef } from "react";
import cardcss from "./Card.module.css";

interface CardProps {
  imageUrl: string;
  title: string;
  body: string;
  onSubmitForm: (contactData: {
    Name: string;
    Email: string;
    Message: string;
  }) => void;
}

function Card(props: CardProps) {
  const [message, setMessage] = useState("");
  const messageInputRef = useRef<HTMLInputElement>(null);

  useEffect(() => {
    if (messageInputRef.current) {
      messageInputRef.current.focus();
      messageInputRef.current.setSelectionRange(0, 0);

      // Delay setting the cursor position
      setTimeout(() => {
        messageInputRef.current?.setSelectionRange(0, 0);
      }, 0);
    }
  }, []);

  const handleFocus = (event: React.FocusEvent<HTMLInputElement>) => {
    event.target.setSelectionRange(0, 0);
  };

  function ContactForm() {
    const nameInputRef = useRef<HTMLInputElement>(null);
    const emailInputRef = useRef<HTMLInputElement>(null);
    const messageInputRef = useRef<HTMLInputElement>(null);
    const submitRef = useRef<HTMLButtonElement>(null);

    function submitHandler(event: React.FormEvent<HTMLFormElement>) {
      event.preventDefault();

      if (
        nameInputRef.current &&
        emailInputRef.current &&
        messageInputRef.current
      ) {
        const nameInput = nameInputRef.current.value;
        const emailInput = emailInputRef.current.value;
        const messageInput = messageInputRef.current.value;

        const contactData = {
          Name: nameInput,
          Email: emailInput,
          Message: messageInput,
        };

        if (submitRef.current) {
          const submittedButton = submitRef.current.value;

          const contactDataWithButton = {
            ...contactData,
            submitbutton: submittedButton,
          };

          // Do something with contactDataWithButton
          props.onSubmitForm(contactData);
        }
      }
    }


    return (
      <div className={cardcss.cardcontainer}>
        <div className={cardcss.imagecontainer}>
          <img src={props.imageUrl} alt="" />
        </div>

        <div className={cardcss.cardtitle}>
          <h3>{props.title}</h3>
        </div>

        <div className={cardcss.cardbody}>
          <p>{props.body}</p>

          <form onSubmit={submitHandler}>
            <div>
              <label className={cardcss.namelabel} htmlFor="name">
                Name:
              </label>
              <input
                className={cardcss.nameinput}
                type="text"
                id="name"
                name="name"
                ref={nameInputRef}
              />
            </div>

            <div className={cardcss.emailcontainer}>
              <label className={cardcss.emaillabel} htmlFor="email">
                Email:
              </label>
              <input
                className={cardcss.emailinput}
                type="text"
                id="email"
                name="email"
                ref={emailInputRef}
              />
            </div>

            <div className={cardcss.messagecontainer}>
              <label className={cardcss.messagelabel} htmlFor="message">
                Message:
              </label>
              <input
                ref={messageInputRef}
                className={cardcss.messageinput}
                type="text"
                id="message"
                name="message"
                value={message}
                onChange={(e) => setMessage(e.target.value)}
                onFocus={handleFocus}
              />

              <button className={cardcss.sendbutton} ref={submitRef}>
                Send
              </button>
            </div>
          </form>
        </div>
      </div>
    );
  }

  return <ContactForm />;
}

export default Card;

contact.tsx(React component that uses the Card component):

import React, { useRef } from "react";
import contactcss from "./Contact.module.css";
import { FontAwesomeIcon } from "@fortawesome/react-fontawesome";
import Card from "../../components/ui/Card";
import {
  faInstagram,
  faFacebook,
  faLinkedin,
  faDiscord,
} from "@fortawesome/free-brands-svg-icons";
import emaillogo from "../../resources/email-logo.png";
import { setApiKey, send } from "@sendgrid/mail";

const Contact: React.FC = () => {
  const inputRef = useRef<HTMLInputElement>(null);

  const handleClick = () => {
    if (inputRef.current) {
      inputRef.current.focus();
      inputRef.current.setSelectionRange(0, 0);
    }
  };

  setApiKey(
    "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"
  ); 

  function submitFormHandler(contactData: {
    Name: string;
    Email: string;
    Message: string;
  }) {
    const { Name, Email, Message } = contactData;

    const msg = {
      to: "paristiffany12@gmail.com",
      from: Email,
      subject: "GDSC Contact Form - New Message",
      text: `Message from: ${Name}\nMessage: ${Message}`,
      html: `<p>Message from: ${Name}</p><p>Message: ${Message}</p>`,
    };

    send(msg)
      .then(() => {
        console.log("Email sent successfully");
      })
      .catch((error) => {
        console.error(error.toString());
      });
  }

  return (
    <div>
      <div>
        <div className={contactcss.background}>
          <img
            className={contactcss.emaillogo}
            src={emaillogo}
            alt="Contact Us"
          />
          <div className={contactcss.contact}>Contact Us</div>
        </div>
      </div>

      <Card
        title="We're keen to hear from you!"
        imageUrl="https://images.app.goo.gl/D6m6hHMnP1gjsKKV7"
        body=""
        onSubmitForm={submitFormHandler}
      />

      <div className={contactcss.whitebackground}>
        <div className={contactcss.socials}>
          <h1>Follow Our Socials</h1>
          <p className={contactcss.socialmedia}>
            Stay connected with GDSC USYD by following us on our social media
            channels:
          </p>
          <div>
            <a href="https://www.instagram.com/gdscusyd/">
              <FontAwesomeIcon
                className={contactcss.instagram}
                icon={faInstagram}
              />
            </a>

            <a href="https://www.facebook.com/gdsc.usyd">
              <FontAwesomeIcon
                className={contactcss.facebook}
                icon={faFacebook}
              />
            </a>

            <a href="https://discord.com/channels/872033047652990986/872033047652990989">
              <FontAwesomeIcon
                className={contactcss.discord}
                icon={faDiscord}
              />
            </a>

            <a href="https://www.linkedin.com/company/gdsc-usyd/">
              <FontAwesomeIcon
                className={contactcss.linkedin}
                icon={faLinkedin}
              />
            </a>
          </div>
        </div>
      </div>
    </div>
  );
};

export default Contact;

indexfirebase.js (Node.js file to handle sending emails):

"use strict";
Object.defineProperty(exports, "__esModule", { value: true });
var express_1 = require("express");
var mail_1 = require("@sendgrid/mail");
var path_browserify_1 = require("path-browserify");
// Set up SendGrid API key
mail_1.default.setApiKey("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");
// Create Express app
var app = (0, express_1.default)();
// Middleware to parse JSON requests
app.use(express_1.default.json());
// Route to handle sending emails
app.post("/send-email", function (req, res) {
    var _a = req.body, Name = _a.Name, Email = _a.Email, Message = _a.Message, submitbutton = _a.submitbutton;
    var msg = {
        to: "paristiffany12@gmail.com",
        from: "gdscusyd@gmail.com",
        subject: "Example Subject",
        text: "Name: ".concat(Name, "\nEmail: ").concat(Email, "\nMessage: ").concat(Message, "\nSubmit Button: ").concat(submitbutton),
        html: "<p>Name: ".concat(Name, "</p><p>Email: ").concat(Email, "</p><p>Message: ").concat(Message, "</p><p>Submit Button: ").concat(submitbutton, "</p>"),
    };
    mail_1.default
        .send(msg)
        .then(function () {
        res.status(200).json({ message: "Email sent successfully" });
    })
        .catch(function (error) {
        console.error(error.toString());
        res.status(500).json({ error: "Failed to send email" });
    });
});
// Set up fallback for path module
path_browserify_1.resolve.fallback = { path: path_browserify_1.require.resolve("path-browserify") };
// Start the server
app.listen(3000, function () {
    console.log("Server started on port 3000");
});

I believe this error is occurring because the fs and path modules are part of the Node.js environment and are not available in a browser environment.

My questions are:

  1. Can I use the SendGrid module in a browser-based React application?
  2. Is it possible to fix this error and make the SendGrid module work in my React application?
  3. If it's not possible, what alternatives can I use to send emails from a contact form in a React application?

I appreciate any help or guidance to resolve this issue. Thank you in advance!

1 Answers1

1

You assumption is right, it is not possible to run nodejs modules on the client. And for security reasons this is also not recommended as it'll expose your API key to the world.

Instead, you need to implement all SendGrid-related code on the server (like express, Next.js or similar)

IObert
  • 2,118
  • 1
  • 10
  • 17