{"content":"# Better Auth Emails\n\nAdd email verification, password reset, and account management emails to Better Auth using Resend.\n\n### Step 1: Create email templates\n\nCreate styled email templates for all auth flows.\n\n**Password Reset**:\n\n```tsx\n// src/lib/auth/emails/forgot-password.tsx\ninterface ForgotPasswordEmailProps {\n  resetLink: string;\n  userName?: string;\n}\n\nexport function ForgotPasswordEmail({\n  resetLink,\n  userName,\n}: ForgotPasswordEmailProps) {\n  return (\n    <div\n      style={{\n        fontFamily:\n          '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n        maxWidth: \"600px\",\n        margin: \"0 auto\",\n        padding: \"40px 20px\",\n        backgroundColor: \"#fafafa\",\n      }}\n    >\n      <div\n        style={{\n          backgroundColor: \"#ffffff\",\n          borderRadius: \"8px\",\n          padding: \"40px\",\n          boxShadow: \"0 2px 8px rgba(0, 0, 0, 0.06)\",\n        }}\n      >\n        <h1\n          style={{\n            fontSize: \"24px\",\n            fontWeight: \"600\",\n            color: \"#1a1a1a\",\n            marginTop: \"0\",\n            marginBottom: \"16px\",\n          }}\n        >\n          Reset your password\n        </h1>\n        <p\n          style={{\n            fontSize: \"16px\",\n            color: \"#4a4a4a\",\n            lineHeight: \"1.6\",\n            marginBottom: \"24px\",\n          }}\n        >\n          {userName ? `Hi ${userName},` : \"Hi,\"} we received a request to reset\n          your password. Click the button below to choose a new password.\n        </p>\n        <a\n          href={resetLink}\n          style={{\n            display: \"inline-block\",\n            backgroundColor: \"#0d9488\",\n            color: \"#ffffff\",\n            padding: \"14px 28px\",\n            borderRadius: \"6px\",\n            textDecoration: \"none\",\n            fontWeight: \"500\",\n            fontSize: \"16px\",\n          }}\n        >\n          Reset Password\n        </a>\n        <p\n          style={{\n            fontSize: \"14px\",\n            color: \"#6b6b6b\",\n            marginTop: \"32px\",\n            lineHeight: \"1.5\",\n          }}\n        >\n          If you didn&apos;t request a password reset, you can safely ignore\n          this email. Your password will remain unchanged.\n        </p>\n        <hr\n          style={{\n            border: \"none\",\n            borderTop: \"1px solid #e5e5e5\",\n            margin: \"32px 0\",\n          }}\n        />\n        <p\n          style={{\n            fontSize: \"12px\",\n            color: \"#9a9a9a\",\n            margin: \"0\",\n          }}\n        >\n          This link will expire in 1 hour. If the button above doesn&apos;t\n          work, copy and paste this URL into your browser:\n        </p>\n        <p\n          style={{\n            fontSize: \"12px\",\n            color: \"#0d9488\",\n            wordBreak: \"break-all\",\n            marginTop: \"8px\",\n          }}\n        >\n          {resetLink}\n        </p>\n      </div>\n    </div>\n  );\n}\n```\n\n**Email Verification**:\n\n```tsx\n// src/lib/auth/emails/verify-email.tsx\ninterface VerifyEmailProps {\n  verificationLink: string;\n  userName?: string;\n}\n\nexport function VerifyEmail({ verificationLink, userName }: VerifyEmailProps) {\n  return (\n    <div\n      style={{\n        fontFamily:\n          '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n        maxWidth: \"600px\",\n        margin: \"0 auto\",\n        padding: \"40px 20px\",\n        backgroundColor: \"#fafafa\",\n      }}\n    >\n      <div\n        style={{\n          backgroundColor: \"#ffffff\",\n          borderRadius: \"8px\",\n          padding: \"40px\",\n          boxShadow: \"0 2px 8px rgba(0, 0, 0, 0.06)\",\n        }}\n      >\n        <h1\n          style={{\n            fontSize: \"24px\",\n            fontWeight: \"600\",\n            color: \"#1a1a1a\",\n            marginTop: \"0\",\n            marginBottom: \"16px\",\n          }}\n        >\n          Verify your email address\n        </h1>\n        <p\n          style={{\n            fontSize: \"16px\",\n            color: \"#4a4a4a\",\n            lineHeight: \"1.6\",\n            marginBottom: \"24px\",\n          }}\n        >\n          {userName ? `Hi ${userName},` : \"Hi,\"} thanks for signing up! Please\n          verify your email address to complete your registration and access all\n          features.\n        </p>\n        <a\n          href={verificationLink}\n          style={{\n            display: \"inline-block\",\n            backgroundColor: \"#0d9488\",\n            color: \"#ffffff\",\n            padding: \"14px 28px\",\n            borderRadius: \"6px\",\n            textDecoration: \"none\",\n            fontWeight: \"500\",\n            fontSize: \"16px\",\n          }}\n        >\n          Verify Email Address\n        </a>\n        <p\n          style={{\n            fontSize: \"14px\",\n            color: \"#6b6b6b\",\n            marginTop: \"32px\",\n            lineHeight: \"1.5\",\n          }}\n        >\n          If you didn&apos;t create an account, you can safely ignore this\n          email.\n        </p>\n        <hr\n          style={{\n            border: \"none\",\n            borderTop: \"1px solid #e5e5e5\",\n            margin: \"32px 0\",\n          }}\n        />\n        <p\n          style={{\n            fontSize: \"12px\",\n            color: \"#9a9a9a\",\n            margin: \"0\",\n          }}\n        >\n          This link will expire in 1 hour. If the button above doesn&apos;t\n          work, copy and paste this URL into your browser:\n        </p>\n        <p\n          style={{\n            fontSize: \"12px\",\n            color: \"#0d9488\",\n            wordBreak: \"break-all\",\n            marginTop: \"8px\",\n          }}\n        >\n          {verificationLink}\n        </p>\n      </div>\n    </div>\n  );\n}\n```\n\n**Change Email Confirmation**:\n\n```tsx\n// src/lib/auth/emails/change-email.tsx\ninterface ChangeEmailProps {\n  confirmationLink: string;\n  newEmail: string;\n  userName?: string;\n}\n\nexport function ChangeEmail({\n  confirmationLink,\n  newEmail,\n  userName,\n}: ChangeEmailProps) {\n  return (\n    <div\n      style={{\n        fontFamily:\n          '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n        maxWidth: \"600px\",\n        margin: \"0 auto\",\n        padding: \"40px 20px\",\n        backgroundColor: \"#fafafa\",\n      }}\n    >\n      <div\n        style={{\n          backgroundColor: \"#ffffff\",\n          borderRadius: \"8px\",\n          padding: \"40px\",\n          boxShadow: \"0 2px 8px rgba(0, 0, 0, 0.06)\",\n        }}\n      >\n        <h1\n          style={{\n            fontSize: \"24px\",\n            fontWeight: \"600\",\n            color: \"#1a1a1a\",\n            marginTop: \"0\",\n            marginBottom: \"16px\",\n          }}\n        >\n          Approve email change\n        </h1>\n        <p\n          style={{\n            fontSize: \"16px\",\n            color: \"#4a4a4a\",\n            lineHeight: \"1.6\",\n            marginBottom: \"16px\",\n          }}\n        >\n          {userName ? `Hi ${userName},` : \"Hi,\"} we received a request to change\n          your email address.\n        </p>\n        <div\n          style={{\n            backgroundColor: \"#f5f5f5\",\n            borderRadius: \"6px\",\n            padding: \"16px\",\n            marginBottom: \"24px\",\n          }}\n        >\n          <p\n            style={{\n              fontSize: \"14px\",\n              color: \"#6b6b6b\",\n              margin: \"0 0 4px 0\",\n            }}\n          >\n            New email address:\n          </p>\n          <p\n            style={{\n              fontSize: \"16px\",\n              color: \"#1a1a1a\",\n              fontWeight: \"500\",\n              margin: \"0\",\n            }}\n          >\n            {newEmail}\n          </p>\n        </div>\n        <p\n          style={{\n            fontSize: \"16px\",\n            color: \"#4a4a4a\",\n            lineHeight: \"1.6\",\n            marginBottom: \"24px\",\n          }}\n        >\n          Click the button below to approve this change. A verification email\n          will then be sent to your new email address.\n        </p>\n        <a\n          href={confirmationLink}\n          style={{\n            display: \"inline-block\",\n            backgroundColor: \"#0d9488\",\n            color: \"#ffffff\",\n            padding: \"14px 28px\",\n            borderRadius: \"6px\",\n            textDecoration: \"none\",\n            fontWeight: \"500\",\n            fontSize: \"16px\",\n          }}\n        >\n          Approve Email Change\n        </a>\n        <p\n          style={{\n            fontSize: \"14px\",\n            color: \"#6b6b6b\",\n            marginTop: \"32px\",\n            lineHeight: \"1.5\",\n          }}\n        >\n          If you didn&apos;t request this change, please ignore this email or\n          contact support if you&apos;re concerned about your account security.\n        </p>\n        <hr\n          style={{\n            border: \"none\",\n            borderTop: \"1px solid #e5e5e5\",\n            margin: \"32px 0\",\n          }}\n        />\n        <p\n          style={{\n            fontSize: \"12px\",\n            color: \"#9a9a9a\",\n            margin: \"0\",\n          }}\n        >\n          This link will expire in 1 hour. If the button above doesn&apos;t\n          work, copy and paste this URL into your browser:\n        </p>\n        <p\n          style={{\n            fontSize: \"12px\",\n            color: \"#0d9488\",\n            wordBreak: \"break-all\",\n            marginTop: \"8px\",\n          }}\n        >\n          {confirmationLink}\n        </p>\n      </div>\n    </div>\n  );\n}\n```\n\n**Delete Account Verification**:\n\n```tsx\n// src/lib/auth/emails/delete-account.tsx\ninterface DeleteAccountEmailProps {\n  confirmationLink: string;\n  userName?: string;\n}\n\nexport function DeleteAccountEmail({\n  confirmationLink,\n  userName,\n}: DeleteAccountEmailProps) {\n  return (\n    <div\n      style={{\n        fontFamily:\n          '-apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, sans-serif',\n        maxWidth: \"600px\",\n        margin: \"0 auto\",\n        padding: \"40px 20px\",\n        backgroundColor: \"#fafafa\",\n      }}\n    >\n      <div\n        style={{\n          backgroundColor: \"#ffffff\",\n          borderRadius: \"8px\",\n          padding: \"40px\",\n          boxShadow: \"0 2px 8px rgba(0, 0, 0, 0.06)\",\n        }}\n      >\n        <h1\n          style={{\n            fontSize: \"24px\",\n            fontWeight: \"600\",\n            color: \"#dc2626\",\n            marginTop: \"0\",\n            marginBottom: \"16px\",\n          }}\n        >\n          Confirm Account Deletion\n        </h1>\n        <p\n          style={{\n            fontSize: \"16px\",\n            color: \"#4a4a4a\",\n            lineHeight: \"1.6\",\n            marginBottom: \"16px\",\n          }}\n        >\n          {userName ? `Hi ${userName},` : \"Hi,\"} we received a request to\n          permanently delete your account.\n        </p>\n        <div\n          style={{\n            backgroundColor: \"#fef2f2\",\n            border: \"1px solid #fecaca\",\n            borderRadius: \"6px\",\n            padding: \"16px\",\n            marginBottom: \"24px\",\n          }}\n        >\n          <p\n            style={{\n              fontSize: \"14px\",\n              color: \"#dc2626\",\n              fontWeight: \"500\",\n              margin: \"0 0 8px 0\",\n            }}\n          >\n            Warning: This action is irreversible\n          </p>\n          <p\n            style={{\n              fontSize: \"14px\",\n              color: \"#7f1d1d\",\n              margin: \"0\",\n            }}\n          >\n            Clicking the button below will permanently delete your account and\n            all associated data. This cannot be undone.\n          </p>\n        </div>\n        <p\n          style={{\n            fontSize: \"16px\",\n            color: \"#4a4a4a\",\n            lineHeight: \"1.6\",\n            marginBottom: \"24px\",\n          }}\n        >\n          If you&apos;re sure you want to proceed, click the button below:\n        </p>\n        <a\n          href={confirmationLink}\n          style={{\n            display: \"inline-block\",\n            backgroundColor: \"#dc2626\",\n            color: \"#ffffff\",\n            padding: \"14px 28px\",\n            borderRadius: \"6px\",\n            textDecoration: \"none\",\n            fontWeight: \"500\",\n            fontSize: \"16px\",\n          }}\n        >\n          Delete My Account\n        </a>\n        <p\n          style={{\n            fontSize: \"14px\",\n            color: \"#6b6b6b\",\n            marginTop: \"32px\",\n            lineHeight: \"1.5\",\n          }}\n        >\n          If you didn&apos;t request this deletion, please ignore this email or\n          contact support immediately. Your account will remain safe.\n        </p>\n        <hr\n          style={{\n            border: \"none\",\n            borderTop: \"1px solid #e5e5e5\",\n            margin: \"32px 0\",\n          }}\n        />\n        <p\n          style={{\n            fontSize: \"12px\",\n            color: \"#9a9a9a\",\n            margin: \"0\",\n          }}\n        >\n          This link will expire in 1 hour. If the button above doesn&apos;t\n          work, copy and paste this URL into your browser:\n        </p>\n        <p\n          style={{\n            fontSize: \"12px\",\n            color: \"#dc2626\",\n            wordBreak: \"break-all\",\n            marginTop: \"8px\",\n          }}\n        >\n          {confirmationLink}\n        </p>\n      </div>\n    </div>\n  );\n}\n```\n\n### Step 2: Update the auth server\n\nUpdate the auth server with email verification, change email, and delete account support:\n\n```tsx\n// src/lib/auth/server.tsx\nimport { betterAuth } from \"better-auth\";\nimport { drizzleAdapter } from \"better-auth/adapters/drizzle\";\nimport { db } from \"../db/client\";\nimport { authConfig } from \"./config\";\nimport { sendEmail } from \"../resend/send\";\nimport { ForgotPasswordEmail } from \"./emails/forgot-password\";\nimport { VerifyEmail } from \"./emails/verify-email\";\nimport { ChangeEmail } from \"./emails/change-email\";\nimport { DeleteAccountEmail } from \"./emails/delete-account\";\n\nexport const auth = betterAuth({\n  secret: authConfig.server.secret,\n  baseURL: authConfig.server.url,\n  database: drizzleAdapter(db, {\n    provider: \"pg\",\n    usePlural: true,\n  }),\n  emailAndPassword: {\n    enabled: true,\n    requireEmailVerification: true,\n    async sendResetPassword({ user, url }) {\n      void sendEmail({\n        to: user.email,\n        subject: \"Reset Your Password\",\n        react: <ForgotPasswordEmail resetLink={url} userName={user.name} />,\n      });\n    },\n  },\n  emailVerification: {\n    sendOnSignUp: true,\n    async sendVerificationEmail({ user, url }) {\n      void sendEmail({\n        to: user.email,\n        subject: \"Verify your email address\",\n        react: <VerifyEmail verificationLink={url} userName={user.name} />,\n      });\n    },\n  },\n  user: {\n    changeEmail: {\n      enabled: true,\n      async sendChangeEmailConfirmation({ user, newEmail, url }) {\n        void sendEmail({\n          to: user.email,\n          subject: \"Approve email change\",\n          react: (\n            <ChangeEmail\n              confirmationLink={url}\n              newEmail={newEmail}\n              userName={user.name}\n            />\n          ),\n        });\n      },\n    },\n    deleteUser: {\n      enabled: true,\n      async sendDeleteAccountVerification({ user, url }) {\n        void sendEmail({\n          to: user.email,\n          subject: \"Confirm account deletion\",\n          react: (\n            <DeleteAccountEmail confirmationLink={url} userName={user.name} />\n          ),\n        });\n      },\n    },\n  },\n});\n```\n\n---\n\n## Usage\n\n### Request Password Reset\n\n```typescript\nimport { authClient } from \"@/lib/auth/client\";\n\nawait authClient.requestPasswordReset({\n  email: \"user@example.com\",\n  redirectTo: \"/reset-password\",\n});\n```\n\n### Reset Password\n\n```typescript\nimport { authClient } from \"@/lib/auth/client\";\n\nawait authClient.resetPassword({\n  newPassword: \"newSecurePassword\",\n  token: \"token-from-url\",\n});\n```\n\n### Send Verification Email\n\n```typescript\nimport { authClient } from \"@/lib/auth/client\";\n\nawait authClient.sendVerificationEmail({\n  email: \"user@example.com\",\n  callbackURL: \"/chats\",\n});\n```\n\n### Change Email\n\n```typescript\nimport { authClient } from \"@/lib/auth/client\";\n\nawait authClient.changeEmail({\n  newEmail: \"newemail@example.com\",\n  callbackURL: \"/profile\",\n});\n```\n\n### Change Password\n\n```typescript\nimport { authClient } from \"@/lib/auth/client\";\n\nawait authClient.changePassword({\n  currentPassword: \"oldPassword\",\n  newPassword: \"newPassword\",\n  revokeOtherSessions: true,\n});\n```\n\n### Delete Account\n\n```typescript\nimport { authClient } from \"@/lib/auth/client\";\n\nawait authClient.deleteUser({\n  password: \"password\",\n  callbackURL: \"/\",\n});\n```\n\n---\n\n## File Structure\n\n```\nsrc/lib/auth/\n  server.tsx   # Already .tsx from base setup\n  emails/\n    forgot-password.tsx\n    verify-email.tsx\n    change-email.tsx\n    delete-account.tsx\n```"}