How To Send Emails Using React Emails, Resend, and Next.js Server Actions

2023-09-18 12:00:00
Learn how to send emails efficiently using React Emails, implement resend functionality, and leverage the power of Next.js Server Actions. Explore a comprehensive guide to streamline your email communication in your web applications.

Introduction

Email communication is a vital aspect of modern web applications, and in this comprehensive guide, we'll show you how to wield its power effectively. Join us as we explore the intricate art of sending emails with React Emails, implementing resend functionality, and harnessing the capabilities of Next.js Server Actions. Whether you're a seasoned developer or just starting, this article will equip you with the knowledge and tools to streamline your email communication, making it an asset rather than a challenge in your web projects. Let's dive in and transform your email game!

First, let's kickstart our journey by initializing a next.js project. We were using npm for this task, but today I've chosen to leverage the blazing speed of Bun ๐Ÿ”ฅ to accelerate our project setup.

bash
cd Desktop #navigating to desktop, I am using macos could be different in Windows so be aware.
bunx create-next-app@latest react-emails-resend-nextjs

After our project has been created. We should navigate to our created project, and open it with vscode. In my case, I'll do that using terminal.

bash
cd react-emails-resend-nextjs #since we are already in desktop directory. We just should navigate to our project directly
code . #open project with vscode.

Installing dependencies

We need to install some dependencies to achieve our goals with this project.

  • React-Email: A collection of high-quality, unstyled components for creating beautiful emails using React and TypeScript.
  • Resend: The best API to reach humans instead of spam folders. Build, test, and deliver transactional emails at scale.
  • React-hook-form: Performant, flexible and extensible forms with easy-to-use validation.
  • Zod: TypeScript-first schema validation with static type inference.
bash
bun add resend react-email react-hook-form zod  #installing our packages

Setup environments

After installing our packages. We now need to create an account in resend and get our email service api key. To do that we will open resend official website, and at top right of the page you can create your account. In my case I chose github to create my account easily.

Now you should create an api in home page of opened dashboard. This page will be opened after you login successfully.

Create Resend API Key

After that copy your API Key.

Copy Resend API Key

Now go to your root directory of your project and create .env.local file to save your API Key in that file.

bash
mkdir .env.local

And update your file like this

.env.local
RESEND_API_KEY="your_resend_api_key_here"
โš ๏ธ

Do not forget to update your global.css file to be like the code below

/src/app/global.css
@tailwind base;
@tailwind components;
@tailwind utilities;

Create Components

Now, we need to create some components needed in our project. So in src/ directory let's create a new folder called components, and create a file called input.tsx

/src/components/input.tsx
import { FC, HTMLAttributes, HTMLInputTypeAttribute } from "react"
import {
    FieldValues,
    UseFormRegister,
    RegisterOptions,
    UseFormRegisterReturn
 
  } from "react-hook-form";
 
type InputProps = {
    label?: string
    type?: HTMLInputTypeAttribute
    register: UseFormRegisterReturn
    error: string | null
} & HTMLAttributes<HTMLInputElement>
 
export const Input:FC<InputProps> = ({
    label,
    register,
    error,
    ...rest
}) => {
  return (
    <div className="flex flex-col gap-1">
        <input
            className="p-2 rounded-lg border border-slate-400 outline-none hover:border-black placeholder:text-gray-600"
            {...register}
            {...rest}
        />
        {error ? (
            <span className="text-red-600 text-sm">
                {error}
            </span>
        ) : null}
 
    </div>
  )
}
 
 
type TextAreaProps = {
    label?: string
    type?: HTMLInputTypeAttribute
    register: UseFormRegisterReturn
    error: string | null
} & HTMLAttributes<HTMLTextAreaElement>
 
export const TextArea:FC<TextAreaProps> = ({
    label,
    register,
    error,
    ...rest
}) => {
  return (
    <div className="flex flex-col gap-1">
        <textarea
            className="p-2 rounded-lg border border-slate-400 outline-none hover:border-black placeholder:text-gray-600"
            rows={4}
            {...register}
            {...rest}
        />
        {error ? (
            <span className="text-red-600 text-sm">
                {error}
            </span>
        ) : null}
 
    </div>
  )
}

Setup Contact Form

Now, let's open our Home Page and update it to be the form that we want;

"use client"
import { Input, TextArea } from "@/components/input";
import { useForm } from "react-hook-form";
import * as z from "zod"
import { zodResolver } from "@hookform/resolvers/zod"
import { useState } from "react";
import { sendEmailAction } from "./_actions";
 
const formSchema = z.object({
  fullName: z.string().nonempty(), // required
  email: z.string().email().nonempty(), // type of email and required
  message: z.string().nonempty() // required
})
 
// Form values type
export type FormProps = z.infer<typeof formSchema>
 
export default function Home() {
  const [data, setData] = useState<{
      success: boolean
      error?:string | null
      loading: boolean
      message?: string
    }>({
    success: false,
    error: null,
    loading: false,
    message: ""
  })
 
  // create form hook
  const {
    register,
    handleSubmit,
    reset,
    formState:{ errors }
  } = useForm<FormProps>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      fullName: "",
      email: "",
      message: ""
    }
  })
 
  // submit function
  const submit = async (values: FormProps) => {
    setData(prev => ({ ...prev, loading: true })) // set loading true for better ux experience
    const data = await sendEmailAction(values); // call server action and send data to it.
    if (data.success) { // if sent successfully
      setData({
        loading: false,
        success: true,
        error: null,
        message: data.message
      })
      reset() // reset form fields
    } else { // if there is an error
      setData({
        loading: false,
        success: false,
        error: data.error,
      })
    }
  }
 
  return (
   <main className="my-20">
    <div className="max-w-xl mx-auto border border-slate-500 shadow-xl p-5">
      <h1 className="text-center font-bold text-2xl mb-5">Send Email</h1>
      <form onSubmit={handleSubmit(submit)}>
        <div className="grid grid-cols-1 gap-2">
          {
            !data.success && data.error ? (
              <div className="my-1 p-2 bg-red-500/25 text-red-500 border border-red-500">
                {data.error}
              </div>
            ) : null
          }
          {
            data.success && !data.error ? (
              <div className="my-1 p-2 bg-green-500/25 text-green-500 border border-green-500">
                {data.message}
              </div>
            ) : null
          }
          <Input
            placeholder="Full Name"
            label="Full Name"
            type="text"
            register={register("fullName")}
            error={errors?.fullName?.message || null}
          />
          <Input
            placeholder="Email"
            label="Email"
            type="text"
            register={register("email")}
            error={errors?.email?.message || null}
          />
          <TextArea
            placeholder="Message"
            label="Message"
            register={register("message")}
            error={errors?.message?.message || null}
          />
 
          <button type="submit" disabled={data.loading} className="bg-black text-white outline-none border-none p-2 rounded-lg disabled:opacity-50">
            {data.loading ? "Sending ..." : "Send"}
          </button>
        </div>
      </form>
    </div>
   </main>
  )
}

Create Email Component

After design our form, now we will create Email Component using react-email. An email template to sen our mails.

First we need to install some dependencies to use in our email template

bash
bun add @react-email/html @react-email/head @react-email/button @react-email/heading @react-email/link @react-email/row @react-email/column @react-email/text @react-email/tailwind

Now let's get our email template ready to use. Note: I will create this file at /src/components with email.tsx name.

/src/components/email.tsx
import { FormProps } from '@/app/page'
import { Html } from '@react-email/html'
import { Button } from '@react-email/button'
import { Heading } from '@react-email/heading'
import { Head } from '@react-email/head'
import { Text } from '@react-email/text'
import { Row } from '@react-email/row'
import { Column } from '@react-email/column'
import { Tailwind } from '@react-email/tailwind' // to allow us use tailwind classes in email template
 
type Props = FormProps // receive contact data
 
const Email = ({ email, fullName, message  }: Props) => {
  return (
    <Tailwind>
        <Html lang='en'>
            <Head>
                <title>Test Resend Email with React Email & Next.js</title>
            </Head>
            <Heading as='h1' className='text-2xl mb-5 font-bold'>Hi! {fullName}</Heading>
            <Row>
                <Column>
                    <Text className='text-md my-2'>Message: {message}</Text>
                </Column>
            </Row>
            <Row>
                <Column>
                    <Text className='text-md my-2'>Your entered email was: {email}</Text>
                </Column>
            </Row>
            <Row>
                <Column>
                    <Button href='https://hamudeshahin.me' className='bg-black text-white rounded-lg px-4 py-2 text-lg'>Test Button</Button>
                </Column>
            </Row>
        </Html>
    </Tailwind>
  )
}
export default Email

Our template is ready tp use now! ๐Ÿฅณ

Create Server Action

Server Actions is a Next.js 13 feature that allows you to execute functions on the backend from the frontend. That mean you do not need to create an api to do some operations on database. Now let's create our server action file in /src/app/_actions.ts

But before creating our server action file. We need to update our next.config.js file.

next.config.js
/** @type {import('next').NextConfig} */
const nextConfig = {
    experimental: {
        serverActions: true,
    },
}
module.exports = nextConfig

Now our server action file must be like this;

/src/app/_actions.ts
'use server' // tell next.js to use this at server-side not on the client-side.
import { Resend } from "resend";
import { FormProps } from "./page"; // import form data type
import Email from "@/components/email";
 
 
const RESEND_API_KEY = process.env.RESEND_API_KEY!
 
console.log('RESEND_API_KEY');
console.log(RESEND_API_KEY);
 
const resend = new Resend(RESEND_API_KEY)
 
// our server action function
export async function sendEmailAction(params:FormProps) {
    try {
        const res = await resend.sendEmail({
            from: 'onboarding@resend.dev',
            to: params.email,
            react: Email(params),
            subject: `Message from ${params.fullName}`
        })
        if (!res.id) {
            return { success: false, error: "Something went wrong!" }
        }
        return { success: true, message: `Email has been sent to ${params.email}` }
    } catch (err) { // catch any unknown error if happen
        console.error(String(err));
        return { success: false, error: String(err) }
    }
}

Testing our Project

Now let's test our project and try to send an email!

Fill Data

Filling Email Form

Response After Success Sending

Success Response

Received Email

Success Response


Get Full Code

You can get full code of this project in my GitHub Repo ๐Ÿ˜„


Conclusion:

In this guide, we've mastered email communication in web applications. By combining React Email,Resend's resend functionality, and Next.js Server Actions, we've turned email management into a breeze.

We initiated our project swiftly with Bun, installed necessary dependencies, and crafted a sleek contact form. Our email template, powered by React Email, adds a professional touch.

Thanks to Next.js Server Actions, some backend operations became effortless. Whether you're a seasoned developer or just starting, this guide equips you to streamline email communication in your web projects.

Now, go ahead, elevate your email game, and create efficient, powerful applications. Unlock new opportunities in your web development journey. ๐Ÿ’ก๐Ÿ“งโœจ #WebDevelopment #React #Nextjs #Emails