Skip to main content
Resend Docs
current

Search documentation

Type to search this documentation.

On this pageOverview

Send Better Auth emails with Resend

Learn how to send Better Auth transactional emails through Resend using email hooks.

Better Auth is a framework-agnostic authentication library for TypeScript. It relies on transactional email for password resets, email verification, magic links, and one-time codes, all of which you can deliver with Resend.

Before you start, you'll need:

npm
npm install resend
pnpm
pnpm add resend
yarn
yarn add resend

Add your API key to the environment:

.env
RESEND_API_KEY=re_xxxxxxxxx

Better Auth calls your sendResetPassword and sendVerificationEmail hooks when a user requests a reset or needs to confirm their address. Send the email with Resend inside each hook.

auth.ts
import { betterAuth } from 'better-auth';
import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

export const auth = betterAuth({
  emailAndPassword: {
    enabled: true,
    sendResetPassword: ({ user, url }) => {
      void resend.emails.send({
        from: 'Acme <onboarding@example.com>',
        to: user.email,
        subject: 'Reset your password',
        html: `Click <a href="${url}">here</a> to reset your password.`,
      });
    },
  },
  emailVerification: {
    sendVerificationEmail: ({ user, url }) => {
      void resend.emails.send({
        from: 'Acme <onboarding@example.com>',
        to: user.email,
        subject: 'Verify your email address',
        html: `Click <a href="${url}">here</a> to verify your email.`,
      });
    },
  },
});

If you use the magic link plugin, send the sign-in link through the sendMagicLink hook.

auth.ts
import { betterAuth } from 'better-auth';
import { magicLink } from 'better-auth/plugins';
import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

export const auth = betterAuth({
  plugins: [
    magicLink({
      sendMagicLink: ({ email, url }) => {
        void resend.emails.send({
          from: 'Acme <onboarding@example.com>',
          to: email,
          subject: 'Sign in to Acme',
          html: `Click <a href="${url}">here</a> to sign in.`,
        });
      },
    }),
  ],
});

The email OTP plugin sends a one-time password (OTP), a numeric code, instead of a link. The type argument tells you which flow triggered the request so you can tailor the subject line.

auth.ts
import { betterAuth } from 'better-auth';
import { emailOTP } from 'better-auth/plugins';
import { Resend } from 'resend';

const resend = new Resend(process.env.RESEND_API_KEY);

export const auth = betterAuth({
  plugins: [
    emailOTP({
      sendVerificationOTP: ({ email, otp, type }) => {
        void resend.emails.send({
          from: 'Acme <onboarding@example.com>',
          to: email,
          subject:
            type === 'sign-in' ? 'Your sign-in code' : 'Your verification code',
          html: `Your code is <strong>${otp}</strong>.`,
        });
      },
    }),
  ],
});

For richer, reusable emails, author your templates with React Email and pass the rendered component to Resend's react field, or reference a Resend template by id.

auth.ts
import { ResetPasswordEmail } from './emails/reset-password';

sendResetPassword: ({ user, url }) => {
  void resend.emails.send({
    from: 'Acme <onboarding@example.com>',
    to: user.email,
    subject: 'Reset your password',
    react: ResetPasswordEmail({ url }),
  });
},

When a flow doesn't behave as expected, check both sides:

  • Better Auth logs show the hook invocation and any errors thrown while sending.
  • Resend Logs show the delivery side: accepted, delivered, opened, bounced.

Together, you get end-to-end visibility across the entire delivery chain.

Suggest an edit

Propose a replacement for this page. The site team reviews it before applying any changes.

Export
Documentation menu