Skip to main content
Resend Docs

Search documentation

Type to search this documentation.

On this pageOverview

Send Better Auth emails with Resend

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