Prerequisites
- Next.js knowledge
- Vercel account
- Github account
- Sengrid account and its API keys
- Sendgrid's npm package
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.
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:
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:
<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.
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.
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
Top comments (7)
Thanks for this Kenneth, it helped a lot! One question though, when I deploy my website to a domain on Vercel, I get a 500 error. I've setup the environment variable on Vercel (I think I've done it correctly?) but I still don't receive emails 🤔
Any ideas?
Thank you so much sir... i find it hard to implement a loading spinner on ChakraUI Button using isSubmitting from react-hook-form.
It didn't work at all
Example:
Thanks for sharing ! Here a package which can help with email sending in node.js environment. One library for many providers.
konfer-be / cliam
Agnostic transactional email sending in Node.js environment
Transactional emails with a kick
Agnostic transactional email sending in Node.js environment💥 💪 💊
> Why ?
To improve and facilitate the implementation, flexibility and maintenance of transactional emailing tasks.
> Features
> Table of contents
> Requirements
> Getting started
Install
> npm i cliam --save
Configure
Create a .cliamrc.json file on the root of your project.
> touch .cliamrc.json
Define a minimalist configuration in .cliamrc.json newly created:
Thank you so much sir... But i tried to add isLoading to ChakraUI button using React-Hook-Form isSubmitting, it does not work at all
Would this not be insecure since there is no back end to hide your token for Sendgrid?
The title and article mentions "Sendgrid". By any chance, did you mean Sendgrid?
If you /api/contact endpoint is public then everybody can request that endpoint and use your smtp server for free. That solution is risky.