How to send serverless emails with Next.js and Sengrid.

Kenneth Coffie

October 16, 2021

5 min read

Prerequisites

One of the biggest disappointments I encountered whilst learning web development, is that you can't send emails on the frontend. I later discovered that you needed a server to accomplish this task. Today, luckily enough for us, there are a variety of technologies and libraries that enables us to achieve this without having to set up a server, hosting, ssh just to send an email.

In this tutorial, I will be accepting emails in my inbox sent to me by anyone on my website. There are 3 main parts to this, the frontend, backend and Sendgrid. The frontend is built on the Next.js which includes features such as SSR (server side rendering), ISR(incremental static regeneration) and SSG(static site generation). The main goal of this article is going to be achieve with another of Next.js known as API routes. API routes or serverless functions are functions that are invoked only when called. In Next.js, api routes sits in your application in the pages folder in a folder named api. Each file in that folder exports a default anonymous function and you can make requests to the route by is by making requests to /api/{filename}. The end result should look something like this.

jsx
export async = (req, res) => {}

If you have ever written a server in express before, the code above should look family because it acts exactly as the route handlers for express routes.

Before you proceed ensure you have stored the API keys in the .env file. It should look like this:

bash
EMAIL_API_KEY="your-sendgrid-api-key"

Form

The first step of sending an email, is designing and developing the form as you wish. In my case. I am using ChakraUI with React-hook-form, but you can use any UI component library of your choice. Here's how my form looks like:

tsx
<form onSubmit={handleSubmit(sendMail)}>
<Stack>
<FormControl >
<FormLabel htmlFor="subject">Subject</FormLabel>
<Input id='subject' type="text" name="subject" {...inputProps} ref={register(setValidation('Subject', false, 2))} />
<ErrorMessage errors={errors} name='subject' as={<Text color='red.600' />} />
</FormControl>
<FormControl>
<FormLabel htmlFor="name" >Name</FormLabel>
<Input id='name' type="name" name="name" ref={register(setValidation('Name'))} {...inputProps} />
<ErrorMessage errors={errors} name='name' as={<Text color='red.600' />} />
</FormControl>
<FormControl>
<FormLabel htmlFor="email" >Email address</FormLabel>
<Input id='email' type="email" name="email" ref={register({ ...setValidation('Email') })} {...inputProps} />
<ErrorMessage errors={errors} name='email' as={<Text color='red.600' />} />
</FormControl>
<FormControl>
<FormLabel htmlFor="message">Message</FormLabel>
<Textarea id='message' type='textarea' name="message" ref={register(setValidation('Message', true))} {...inputProps} h={300} resize='none' />
<ErrorMessage errors={errors} name='message' as={<Text color='red.600' />} />
</FormControl>
<FormControl>
<Button type='submit' color='white' bg='gray.900' width='100%' h={55} mt={5}>
Submit
</Button>
</FormControl>
</Stack>
</form>

Send request to api route

Now send the data from the form to the api route.

tsx
const sendMail = async (data) => {
try {
await fetch("/api/contact", {
"method": "POST",
"headers": { "content-type": "application/json" },
"body": JSON.stringify(data)
})
//if sucess do whatever you like, i.e toast notification
setTimeout(() => reset(), 2000);
} catch (error) {
// toast error message. whatever you wish
}
}

Receiving email and sending it off

Once you have sent the email from your frontend, its time to capture it and send it to yourself. This is achieved by first installing sengrid' via npm install @sendgrid/mail. Once done, ensure you have created a contact.js in folder api folder and paste this in.

tsx
import sgMail from '@sendgrid/mail'
import { NextApiRequest, NextApiResponse } from 'next';
sgMail.setApiKey(process.env.EMAIL_API_KEY);
export default async (req: NextApiRequest, res: NextApiResponse) => {
const { email, subject, message, name } = req.body
const msg = {
to: '<your-email@example.com',
from: email,
subject,
name,
text: message,
};
try {
await sgMail.send(msg);
res.json({ message: `Email has been sent` })
} catch (error) {
res.status(500).json({ error: 'Error sending email' })
}
}

Thats it, the code for sending the email via Sendgrid is quite simple and self explanatory. We first set the api key for Sengrid and after, we create our handler for the route and extract out the email, subject, message and name and then wrap our sgMail.send in a try/catch block.

Deployment

Before deployment, ensure you code is up on Github or Gitlab, also test it and make sure it runs well locally .After these steps, log into your vercel account and start a new project with that Github repo. And thats it, Vercel will build your app and deploy it statically and give you a url to view the project.

Conclusion

You can repurpose the knowledge and send any type of email you want. Sendgrid allows you to create custom and great looking emails using templates. You can also swap Sengrid for your favourite transactional email sending tools such Mailgun, Postmark, Amazon ses or even use gmail via nodemailers SMTP support

If you like this post, please sign up for my newsletter at https://buttondown.email/kennymark or visit my website for more info at https://kennymark.com

Subscribe to my newsletter

You won't receive any spam! ✌️