Creating a Contact Form with React and EmailJS
I’ve always loved the simplicity of Contact Forms and being able to connect with a service or provider through a message and not having to wait for an agent to hop on a Chat box. Mind you, I do understand waiting for an email can also be frustrating but let’s save that for another article.
So while deciding wether I wanted to create a Chatbot or a simple contact form I looked online to help me find an answer to my problem. I ran into some articles and one specifically on EmailJS. EmailJS allows you to send emails directly using JavaScript without having to create a backend. They work with only three steps! First, choose your provider from a variety of email services. Second, create an email template with the data you’d like to collect. And third, add the SDK. That simple! And is completely free up to 200 emails per month!
So let’s walk you through creating an account at EmailJS. Create a free account, log in and select Add New Service. Select the email provider of your choice, fill out the configuration and Connect Account. Once the service has been created you’ll be provided with a Service ID (Save this). Go over to Email Templates and Create A New Template. This is a great feature, it allows you to take the same variable name to collect the data directly from your React form. Additionally, you can add attachments, add CAPTCHA, switch email providers without any code changes, have a history log and much more!
So to begin lets install a few things(I typically use yarn, but npm is great also!)
$ yarn add emailjs-com
$ npm install emailjs-com --save
or using bower
$ bower install emailjs-com --save
Browser Script
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/emailjs-com@2/dist/email.min.js">
</script>
<script type="text/javascript">
(function(){
emailjs.init("YOUR_USER_ID");
})();
</script>
If you need to get your User ID head over to the Integration tab.
Let’s create a react app with a form.
$ npx create-react-app react-emailjs-app
Now that we have the app created. Create a new file, ContactForm.js
$ touch ContactForm.js
import React from 'react';const ContactForm = () => {
return (
<div>
Contact Form
</div>
);
};export default ContactForm;
Let’s go ahead and add that to our App.js file
import React from 'react';
import ContactForm from './ContactForm.js';const App = () => {
return (
<div>
<ContactForm />
</div>
);
};export default App;
We will be using React Hooks to handle the state and effects in our component. useState to handle our state, and useEffects to handle the effects of our React Component. So let’s import useState and useEffect and create the schema of our form.
import React, { useState, useEffect } from 'react';
We want to be able to grab the name and email and as well as make sure the fields are required:
const schema = {
name: {
presence: { allowEmpty: false, message: 'is required' },
length: { maximum: 128 },
},
email: {
presence: { allowEmpty: false, message: 'is required' },
email: true,
length: { maximum: 300 },
};
};
Let’s initialize the values, add a boolean to the input and call our errors.
const [formState, setFormState] = useState({
isValid: false,
values: {},
touched: {},
errors: {},
});
Now let’s get our form together. We will be using Material-UI to make the appearance of our form a bit more appealing. We’ll be using validate.js to make sure our forms are filled out correctly and we’ll adding a few attributes to each input field, including name, id and value to send data to our email.
Let’s add validate.js and material-ui:
yarn add validate.js
yarn add @material-ui/core
Let’s import the new packages into our app:
import { Typography, Grid, Button, TextField } from '@material-ui/core';
import validate from 'validate.js';
Now let’s create our form:
<div className={classes.root}>
<form
headers='application/json'
name="contact-form"
>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h4" align="center">
<strong>Contact Form</strong>
</Typography>
<Typography
variant="h6"
color="textSecondary"
align="center"
>
Contact Us
</Typography>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
placeholder="Name"
label="Name *"
variant="outlined"
size="medium"
name="name"
id="name"
value={formState.values.name}
/>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
placeholder="E-mail"
label="E-mail *"
variant="outlined"
size="medium"
name="email"
id="email"
type="email"
value={formState.values.email}
/>
</Grid>
<Button
size="large"
variant="contained"
type="submit"
color="primary"
>
Send
</Button>
</Grid>
</Grid>
</form>
</div>
Now that we have our form set up lets use useEffect to check the status of the values.
useEffect(() => {
const errors = validate(formState.values, schema);
setFormState(formState => ({
...formState,
isValid: errors ? false : true,
errors: errors || {},
}));
}, [formState.values]);
We want to make sure the data cannot be submitted with blank fields so we can check for that with isValid:
<Button
size="large"
variant="contained"
type="submit"
color="primary"
disabled={!formState.isValid}
>
We will create two new functions to display errors and handle the change to our form input.
const handleChange = (e) => {
e.persist(); setFormState(formState => ({
...formState,
values: {
...formState.values,
[e.target.name]:
e.target.type === 'checkbox'
? e.target.checked
: e.target.value,
},
touched: {
...formState.touched,
[e.target.name]: true,
},
}));
};const hasError = (field) =>
formState.touched[field] && formState.errors[field]
? true
: false;
Let’s add some more attributes to our form. We’ll be using conditional rendering to check if a field is blank, has an error and if so, display the error.
<div className={classes.root}>
<form
headers='application/json'
name="contact-form"
>
<Grid container spacing={2}>
<Grid item xs={12}>
<Typography variant="h4" align="center">
<strong>Contact Form</strong>
</Typography>
<Typography
variant="h6"
color="textSecondary"
align="center"
>
Contact Us
</Typography>
</Grid>
<Grid item xs={12} sm={6}>
<TextField
placeholder="Name"
label="Name *"
variant="outlined"
size="medium"
name="name"
id="name"
value={formState.values.name}
helperText={
hasError('name') ? formState.errors.name[0] : null
}
error={hasError('name')}
onChange={handleChange}
value={formState.values.name || ''} />
</Grid>
<Grid item xs={12} sm={6}>
<TextField
placeholder="E-mail"
label="E-mail *"
variant="outlined"
size="medium"
name="email"
id="email"
type="email"
value={formState.values.email}
helperText={
hasError('email') ? formState.errors.email[0] : null
}
error={hasError('email')}
onChange={handleChange}
value={formState.values.email || ''}
/>
</Grid>
<Button
size="large"
variant="contained"
type="submit"
color="primary"
disabled={!formState.isValid}
>
Send
</Button>
</Grid>
</Grid>
</form>
</div>
Great! Now we’ve got our form all set up!
All that’s left to do is hook up EmailJS. We’ve installed the package and the browser script so lets get it hooked up to start getting our leads from our form.
P.S. I suggest you use an .env file to store your keys so none of your keys get uploaded to your site.
import emailjs from 'emailjs-com';
Add your keys and ID’s:
const USER_ID = process.env.REACT_APP_EMAILJS_USERID
const TEMPLATE_ID = process.env.REACT_APP_EMAILJS_TEMPLATEID
const SERVICE_ID = process.env.REACT_APP_EMAILJS_SERVICEID
Now inside our ContactForm component, let’s create our sendEmail function.
const sendEmail = (e) => {
e.preventDefault(); emailjs.sendForm(
process.env.REACT_APP_EMAILJS_SERVICEID,
process.env.REACT_APP_EMAILJS_TEMPLATEID,
e.target,
process.env.REACT_APP_EMAILJS_SERVICEID
)
.then((res) => console.log('SUCCESS!', res.status, res.text))
.catch(error => console.log('FAILED...', error));
}
add our onSubmit attribute to our form and boom! We’re done! We are now going to be getting leads from our contact form!
<form
headers='application/json'
name="contact-form"
onSubmit={sendEmail}
>
Full Code:
Customize the form more! Grab more data! Get everything you need out of your leads! Enjoy your form!