DEV Community

Chris Walker
Chris Walker

Posted on

Next.js- Sending Email with Resend from Server Action

I recently used the services of Resend, "The API for developers," to send email from my app. It's interesting to me because it involves very little setup, has a fairly generous free tier, and it handles all of the mail hosting: you just bring your own domain.

I wanted to quickly go over how I used it to send emails in my app. There is fairly comprehensive documentation and a quickstart guide on their website for sending emails in next.js, but I went a slightly different way.

The docs say to set up an API route that leverages the Resend sdk. So I did that, and then I realized I was handling my form submission, which used a server action to send a fetch request to my internal api, which extracted the data from the request, passed it to the sdk, and then sent a response back. That seemed like a lot of extra steps. So I ended up calling Resend directly from the server action, and it seemed to simplify things a lot.

To add this to your Next.js app, you would first need to set up an account on Resend, and generate an API key which you store in your .env file.

1. Install

npm install resend

2. Set up an Email Template

So far we haven't diverged from the quick start guide. Create a component to use as the template: components/email-template.tsx


interface EmailTemplateProps {
  firstName: string;
}

export function EmailTemplate({ firstName }: EmailTemplateProps) {
  return (
    <div>
      <h1>Welcome, {firstName}!</h1>
    </div>
  );
}
Enter fullscreen mode Exit fullscreen mode

3. Send Email Using React

This is what I did differently from the guide. Instead of setting up an API route, create a server action in lib/actions/email.ts

"use server";
import { z } from "zod";
import { Resend } from "resend";
import { EmailTemplate } from "@/components/EmailTemplate";
import { getUserFromSession } from "@/lib/auth"
interface DataResponse {
  message: string;
  status: boolean;
}

const schema = z.object({
  firstname: z.string({
    invalid_type_error: "Invalid Email",
  }),
}); //Or whatever you use to validate form data

const sendEmail = async (
  formData: FormData
): Promise<DataResponse> => {

  try {
    const user = await getUserFromSession() 
    if (!user) throw new Error('Unauthorized')
    //Or whatever you use to verify auth in server actions
    const firstName = schema.safeParse({
      firstname: formData.get("firstname"),
    });
    //return early if validation failed
    if (!firstName.success)
      return { 
        message: firstName.errors.flatten().fieldErrors, 
        status: false 
      };

    //instantiate Resend sdk using your API key
    const resend = new Resend(process.env.EMAIL_KEY);

    /* You can send an email to your own email address as a  
       test using this 'from' address without attaching your 
       own domain. Once you have your domain, you would put:
       from: "Your Name <your-address@your-domain.com>"" */
    const { data, error } = await resend.emails.send({
      from: "Acme <onboarding@resend.dev>",
      to: ["delivered@resend.dev"],
      subject: "Hello world",
      react: EmailTemplate({ firstName }),
    });

    if (error) {
      return { message: error.message, status: false };
    }
    return { 
      message: `Email sent to ${firstName}`, 
      status: false 
    };
  } catch (error) {
    return { message: error.message, status: false };
  }
};
Enter fullscreen mode Exit fullscreen mode

4. Try it out

Make a component with a form that uses this action:

'use client'

import { sendEmail } from '@/lib/actions'

export function SendEmailForm() {

  return (
    <form action={sendEmail}>
      <input type="text" name="firstname" />
      <button type="submit">Send Email</button>
    </form>
  )
}
Enter fullscreen mode Exit fullscreen mode

So that's pretty much it. You can of course add a lot more functionality but this is the very basics. I just like this approach slightly better because I try not to set up API routes in my Next.js 13+ projects if I can help it. I just seems to me it kind of defeats the purpose of React Server Components.

Let me know if you have a different idea or have any comments. I hope someone found this interesting.

Top comments (0)