DEV Community

Cover image for Simple contact form with NextJS & EmailJS
Richard S Prins Jr.
Richard S Prins Jr.

Posted on

Simple contact form with NextJS & EmailJS

Contact forms are like a direct line from your users to you, and more often than not provide a ton of insight into how your users perceive and communicate with your business, especially new customers. While you may think that creating a contact form is as easy as some inputs and some server to send off whatever data you provide it, there are a little more subtle nuances to making sure that your form works as expected, especially in production. At least, that is what I found out today while working on a contact page for my new service, SquadPerks.

I was looking for some managed solution that leveraged my existing email service, address, and meshed well with my front end. Enter EmailJS. EmailJS offers complete support for a few of the most popular web frameworks, or just plain vanilla JavaScript, and comes with a nice dashboard that is quick and easy to get a contact service set up, using SMTP mail servers or existing mail services, in my case, Gmail. Just a few details to configure, and you are off to the races.

EmailJS also offers built-in support for reCAPTCHA, which in my opinion is a bulletproof way to prevent form abuse, and can be found on almost any modern website. Unless you want your email quota to be used up by some malicious bot and leave your server gasping for a break, some form of validation is necessary. I went with a simple "I am not a robot" check to verify my users were indeed carbon-based humans.๐Ÿ˜…

All I needed was some way to use reCAPTCHA within a React application, and react-recaptcha was more than enough to get me rolling. They offer a simple reCAPTCHA component that just requires a couple of config props and 2 callback functions to help verify a user's input. We also need to add a script to our <Head> in our _document.js file to use the Google reCAPTCHA CDN.

<script src="https://www.google.com/recaptcha/api.js?&render=explicit" async defer></script>
Enter fullscreen mode Exit fullscreen mode

Recaptcha Component

<Recaptcha
  sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY}
  render="explicit"
  onloadCallback={recaptchaLoaded}
  verifyCallback={verifiedRecaptcha}
/>
Enter fullscreen mode Exit fullscreen mode

The first is a function that, once the reCAPTCHA element is loaded, is called and, with the help of some React local state, becomes true and allows the next callback function to be called.

onLoadCallback

const [recaptchaLoad, setRecaptchaLoad] = React.useState(false)

const recaptchaLoaded = () => {
  setRecaptchaLoad(true);
}
Enter fullscreen mode Exit fullscreen mode

The second callback is invoked once the server returns a response giving an ok that the reCAPTCHA step for the user came back successful, and again, with some local state, can be used as a check before sending off the form submission.

isVerifiedCallback

const [recaptchaLoad, setRecaptchaLoad] = React.useState(false)

const verifiedRecaptcha = (response) => {
  if (response) {
    setIsVerified(true);
  }
}
Enter fullscreen mode Exit fullscreen mode

Now, before this can all be finished, we need some form to place it all into, and luckily EmailJS offers some boilerplate to get us started. Their boilerplate uses the sendForm() method, but for a simple form, the send() method will do. All we need now are our Service ID, an EmailJS Template ID (you'll want to create some simple email template in your EMAILJS dashboard to acquire this).
The best place for these secrets are in a .env file. Once you have gathered your environment variables, you need to shape the data that will be sent from the form to the email server. We can create a templateParams object to hold this information. We need a from_name for our sender, a from_email in case we want to reply, a to_name so that our user knows who they are communicating with, and the message body which holds the message content.
NOTE: if you expect your response to be in an html format, you will want to replace message with message_html, or else your html structured response will end up as a string.

const templateParams = {
  from_name: nameRef.current.value,
  from_email: emailRef.current.value,
  to_name: 'SquadPerks Support',
  message: messageRef.current.value,
}
Enter fullscreen mode Exit fullscreen mode

Now, with the help of some gorgeous styling and components from Chakra UI, we can build our form component to be rendered on our page.

import {
  Input,
  FormControl,
  FormLabel,
  Button,
  Textarea,
  Box,
  Flex
} from '@chakra-ui/react'

<Flex flexDirection="column" >
    <form className="contact-form" onSubmit={sendEmail}>
      <FormLabel>Name</FormLabel>
      <Input type="text" name="user_name" className="from-name" autoComplete={false} ref={nameRef} />
      <FormLabel>Email</FormLabel>
      <Input type="email" name="user_email" className="from-email" autoComplete={false} ref={emailRef} />
      <FormLabel>Message</FormLabel>
      <Textarea name="message" autoComplete={false} ref={messageRef} />
      <Box mt="4">
        <Recaptcha
          sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY}
          render="explicit"
          onloadCallback={recaptchaLoaded}
          verifyCallback={verifiedRecaptcha}
        />
      </Box>
      <Button type="submit" value="Send" className="submit" bgColor="brand.100" mt="4">Send</Button>
    </form>
  </Flex>
Enter fullscreen mode Exit fullscreen mode

And, with that our form is complete! Make sure to add your site domains/localhost/127.0.0 to your reCAPTCHA admin dashboard, or else it won't be able to communicate with your site. Our final code should look something like this:

ContactUsForm.js

import * as React from 'react';
import * as emailjs from 'emailjs-com'
import Recaptcha from 'react-recaptcha'
import {
  Input,
  FormControl,
  FormLabel,
  Button,
  Textarea,
  Box,
  Flex
} from '@chakra-ui/react'

export default function ContactUs() {
  const [recaptchaLoad, setRecaptchaLoad] = React.useState(false)
  const [isVerified, setIsVerified] = React.useState(false)
  const nameRef = React.useRef(null)
  const emailRef = React.useRef(null)
  const messageRef = React.useRef(null)


  const recaptchaLoaded = () => {
    setRecaptchaLoad(true);
  }

  const verifiedRecaptcha = (response) => {
    if (response) {
      setIsVerified(true);
    }
  }
  function sendEmail(e) {
    e.preventDefault();
    const templateParams = {
      from_name: nameRef.current.value,
      from_email: emailRef.current.value,
      to_name: 'SquadPerks Support',
      message: messageRef.current.value,
    }
    if (recaptchaLoad && isVerified) {
      emailjs.send(
        process.env.NEXT_PUBLIC_SERVICE_ID,
        process.env.NEXT_PUBLIC_TEMPLATE_ID,
        templateParams,
        process.env.NEXT_PUBLIC_EMAILJS_USER_ID
      )
        .then((result) => {
          console.log(result.text);
          nameRef.current.value = null
          emailRef.current.value = null
          messageRef.current.value = null
        }, (error) => {
          console.log(error.text);
        });
    } else {
      console.error('Please check reCaptcha and try again.')
    }
  }

  return (
    <>
      <Flex flexDirection="column" >
        <form className="contact-form" onSubmit={sendEmail}>
          <FormLabel>Name</FormLabel>
          <Input type="text" name="user_name" className="from-name" autoComplete={false} ref={nameRef} />
          <FormLabel>Email</FormLabel>
          <Input type="email" name="user_email" className="from-email" autoComplete={false} ref={emailRef} />
          <FormLabel>Message</FormLabel>
          <Textarea name="message" autoComplete={false} ref={messageRef} />
          <Box mt="4">
            <Recaptcha
              sitekey={process.env.NEXT_PUBLIC_RECAPTCHA_SITE_KEY}
              render="explicit"
              onloadCallback={recaptchaLoaded}
              verifyCallback={verifiedRecaptcha}
            />
          </Box>
          <Button type="submit" value="Send" className="submit" bgColor="brand.100" mt="4">Send</Button>
        </form>
      </Flex>
      <style>{`

      `}</style>
    </>
  );
}
Enter fullscreen mode Exit fullscreen mode

Thank you for taking the time to read this post, as it is actually my very first blog post here! I will be sharing many more in the near future as I build my gaming service platform SquadPerks. If you are into gaming and competitive eSports, be sure to check us out on our site and give us a follow on all of our social media! We have a thriving discord community as well, and there is always a place for everyone!

Until next time!

Top comments (5)

Collapse
 
tomekponiat profile image
Tomek Poniatowicz

Its even easier with mailik.dev/

import Mailik from "@mailik/sdk";

type MailInputType = { body: string; subject: string; replyTo: string };

const handleSubmit = (values: MailInputType) => {
  const res = await Mailik(YOURS_API_KEY).send({
    replyTo: data.email,
    body: `
          Name: ${data.name},
          Message: ${data.message},
          Phone:${data.phone}
        `,
    subject: "Mail subject",
  });
};
Enter fullscreen mode Exit fullscreen mode
Collapse
 
christianquispe profile image
Christian Quispe Camasca

Thanks!!!

Collapse
 
richardsprins profile image
Richard S Prins Jr.

No problem, glad you found this useful!

Collapse
 
sidhhh profile image
Sidhhh

thanks alot!

Collapse
 
richardsprins profile image
Richard S Prins Jr.

Thank you for reading! Glad it could help!