@better-auth-ui/react 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (250) hide show
  1. package/dist/components/auth/auth-provider.d.ts +32 -0
  2. package/dist/components/auth/auth-provider.js +44 -0
  3. package/dist/components/email/email-changed.d.ts +97 -0
  4. package/dist/components/email/email-changed.js +70 -0
  5. package/dist/components/email/email-localization.d.ts +61 -0
  6. package/dist/components/email/email-localization.js +24 -0
  7. package/dist/components/email/email-styles.d.ts +89 -0
  8. package/dist/components/email/email-styles.js +129 -0
  9. package/dist/components/email/email-verification.d.ts +97 -0
  10. package/dist/components/email/email-verification.js +75 -0
  11. package/dist/components/email/index.d.ts +9 -0
  12. package/dist/components/email/index.js +9 -0
  13. package/dist/components/email/magic-link.d.ts +93 -0
  14. package/dist/components/email/magic-link.js +72 -0
  15. package/dist/components/email/new-device.d.ts +123 -0
  16. package/dist/components/email/new-device.js +90 -0
  17. package/dist/components/email/otp-email.d.ts +88 -0
  18. package/dist/components/email/otp-email.js +65 -0
  19. package/dist/components/email/password-changed.d.ts +96 -0
  20. package/dist/components/email/password-changed.js +76 -0
  21. package/dist/components/email/reset-password.d.ts +90 -0
  22. package/dist/components/email/reset-password.js +68 -0
  23. package/dist/components/icons/apple.d.ts +8 -0
  24. package/dist/components/icons/apple.js +10 -0
  25. package/dist/components/icons/atlassian.d.ts +10 -0
  26. package/dist/components/icons/atlassian.js +14 -0
  27. package/dist/components/icons/cognito.d.ts +8 -0
  28. package/dist/components/icons/cognito.js +10 -0
  29. package/dist/components/icons/discord.d.ts +8 -0
  30. package/dist/components/icons/discord.js +10 -0
  31. package/dist/components/icons/dropbox.d.ts +8 -0
  32. package/dist/components/icons/dropbox.js +10 -0
  33. package/dist/components/icons/facebook.d.ts +10 -0
  34. package/dist/components/icons/facebook.js +12 -0
  35. package/dist/components/icons/figma.d.ts +11 -0
  36. package/dist/components/icons/figma.js +13 -0
  37. package/dist/components/icons/github.d.ts +10 -0
  38. package/dist/components/icons/github.js +12 -0
  39. package/dist/components/icons/gitlab.d.ts +8 -0
  40. package/dist/components/icons/gitlab.js +10 -0
  41. package/dist/components/icons/google.d.ts +8 -0
  42. package/dist/components/icons/google.js +10 -0
  43. package/dist/components/icons/huggingface.d.ts +8 -0
  44. package/dist/components/icons/huggingface.js +10 -0
  45. package/dist/components/icons/index.d.ts +35 -0
  46. package/dist/components/icons/index.js +35 -0
  47. package/dist/components/icons/kakao.d.ts +10 -0
  48. package/dist/components/icons/kakao.js +12 -0
  49. package/dist/components/icons/kick.d.ts +10 -0
  50. package/dist/components/icons/kick.js +12 -0
  51. package/dist/components/icons/line.d.ts +8 -0
  52. package/dist/components/icons/line.js +10 -0
  53. package/dist/components/icons/linear.d.ts +8 -0
  54. package/dist/components/icons/linear.js +10 -0
  55. package/dist/components/icons/linkedin.d.ts +8 -0
  56. package/dist/components/icons/linkedin.js +10 -0
  57. package/dist/components/icons/microsoft.d.ts +8 -0
  58. package/dist/components/icons/microsoft.js +10 -0
  59. package/dist/components/icons/naver.d.ts +8 -0
  60. package/dist/components/icons/naver.js +10 -0
  61. package/dist/components/icons/notion.d.ts +8 -0
  62. package/dist/components/icons/notion.js +10 -0
  63. package/dist/components/icons/paybin.d.ts +7 -0
  64. package/dist/components/icons/paybin.js +9 -0
  65. package/dist/components/icons/paypal.d.ts +8 -0
  66. package/dist/components/icons/paypal.js +10 -0
  67. package/dist/components/icons/polar.d.ts +8 -0
  68. package/dist/components/icons/polar.js +10 -0
  69. package/dist/components/icons/railway.d.ts +8 -0
  70. package/dist/components/icons/railway.js +10 -0
  71. package/dist/components/icons/reddit.d.ts +8 -0
  72. package/dist/components/icons/reddit.js +10 -0
  73. package/dist/components/icons/roblox.d.ts +11 -0
  74. package/dist/components/icons/roblox.js +13 -0
  75. package/dist/components/icons/salesforce.d.ts +8 -0
  76. package/dist/components/icons/salesforce.js +10 -0
  77. package/dist/components/icons/slack.d.ts +8 -0
  78. package/dist/components/icons/slack.js +10 -0
  79. package/dist/components/icons/spotify.d.ts +8 -0
  80. package/dist/components/icons/spotify.js +10 -0
  81. package/dist/components/icons/tiktok.d.ts +9 -0
  82. package/dist/components/icons/tiktok.js +11 -0
  83. package/dist/components/icons/twitch.d.ts +8 -0
  84. package/dist/components/icons/twitch.js +13 -0
  85. package/dist/components/icons/vercel.d.ts +8 -0
  86. package/dist/components/icons/vercel.js +10 -0
  87. package/dist/components/icons/vk.d.ts +8 -0
  88. package/dist/components/icons/vk.js +10 -0
  89. package/dist/components/icons/wechat.d.ts +8 -0
  90. package/dist/components/icons/wechat.js +10 -0
  91. package/dist/components/icons/x.d.ts +8 -0
  92. package/dist/components/icons/x.js +10 -0
  93. package/dist/components/icons/zoom.d.ts +8 -0
  94. package/dist/components/icons/zoom.js +12 -0
  95. package/dist/components/settings/account/theme-preview.d.ts +3 -0
  96. package/dist/components/settings/account/theme-preview.js +10 -0
  97. package/dist/core.d.ts +1 -0
  98. package/dist/core.js +1 -0
  99. package/dist/hooks/auth/use-auth-mutation.d.ts +18 -0
  100. package/dist/hooks/auth/use-auth-mutation.js +10 -0
  101. package/dist/hooks/auth/use-auth-query.d.ts +24 -0
  102. package/dist/hooks/auth/use-auth-query.js +10 -0
  103. package/dist/hooks/auth/use-authenticate.d.ts +9 -0
  104. package/dist/hooks/auth/use-authenticate.js +28 -0
  105. package/dist/hooks/auth/use-forgot-password.d.ts +20 -0
  106. package/dist/hooks/auth/use-forgot-password.js +14 -0
  107. package/dist/hooks/auth/use-request-password-reset.d.ts +26 -0
  108. package/dist/hooks/auth/use-request-password-reset.js +14 -0
  109. package/dist/hooks/auth/use-reset-password.d.ts +40 -0
  110. package/dist/hooks/auth/use-reset-password.js +13 -0
  111. package/dist/hooks/auth/use-send-verification-email.d.ts +24 -0
  112. package/dist/hooks/auth/use-send-verification-email.js +13 -0
  113. package/dist/hooks/auth/use-session.d.ts +9 -0
  114. package/dist/hooks/auth/use-session.js +18 -0
  115. package/dist/hooks/auth/use-sign-in-email.d.ts +53 -0
  116. package/dist/hooks/auth/use-sign-in-email.js +24 -0
  117. package/dist/hooks/auth/use-sign-in-magic-link.d.ts +32 -0
  118. package/dist/hooks/auth/use-sign-in-magic-link.js +13 -0
  119. package/dist/hooks/auth/use-sign-in-social.d.ts +93 -0
  120. package/dist/hooks/auth/use-sign-in-social.js +13 -0
  121. package/dist/hooks/auth/use-sign-out.d.ts +19 -0
  122. package/dist/hooks/auth/use-sign-out.js +24 -0
  123. package/dist/hooks/auth/use-sign-up-email.d.ts +526 -0
  124. package/dist/hooks/auth/use-sign-up-email.js +24 -0
  125. package/dist/hooks/auth/use-user.d.ts +513 -0
  126. package/dist/hooks/auth/use-user.js +14 -0
  127. package/dist/hooks/settings/use-account-info.d.ts +17 -0
  128. package/dist/hooks/settings/use-account-info.js +25 -0
  129. package/dist/hooks/settings/use-change-email.d.ts +24 -0
  130. package/dist/hooks/settings/use-change-email.js +23 -0
  131. package/dist/hooks/settings/use-change-password.d.ts +54 -0
  132. package/dist/hooks/settings/use-change-password.js +11 -0
  133. package/dist/hooks/settings/use-delete-user.d.ts +42 -0
  134. package/dist/hooks/settings/use-delete-user.js +11 -0
  135. package/dist/hooks/settings/use-link-social.d.ts +46 -0
  136. package/dist/hooks/settings/use-link-social.js +10 -0
  137. package/dist/hooks/settings/use-list-accounts.d.ts +20 -0
  138. package/dist/hooks/settings/use-list-accounts.js +23 -0
  139. package/dist/hooks/settings/use-list-device-sessions.d.ts +11 -0
  140. package/dist/hooks/settings/use-list-device-sessions.js +22 -0
  141. package/dist/hooks/settings/use-list-sessions.d.ts +20 -0
  142. package/dist/hooks/settings/use-list-sessions.js +22 -0
  143. package/dist/hooks/settings/use-revoke-multi-session.d.ts +19 -0
  144. package/dist/hooks/settings/use-revoke-multi-session.js +20 -0
  145. package/dist/hooks/settings/use-revoke-session.d.ts +19 -0
  146. package/dist/hooks/settings/use-revoke-session.js +21 -0
  147. package/dist/hooks/settings/use-set-active-session.d.ts +37 -0
  148. package/dist/hooks/settings/use-set-active-session.js +28 -0
  149. package/dist/hooks/settings/use-unlink-account.d.ts +21 -0
  150. package/dist/hooks/settings/use-unlink-account.js +21 -0
  151. package/dist/hooks/settings/use-update-user.d.ts +11 -0
  152. package/dist/hooks/settings/use-update-user.js +29 -0
  153. package/dist/hooks/use-hydrated.d.ts +6 -0
  154. package/dist/hooks/use-hydrated.js +18 -0
  155. package/dist/index.d.ts +32 -0
  156. package/dist/index.js +33 -0
  157. package/dist/lib/auth-client.d.ts +1979 -0
  158. package/dist/lib/auth-client.js +5 -0
  159. package/dist/lib/auth-config.d.ts +43 -0
  160. package/dist/lib/auth-config.js +1 -0
  161. package/dist/lib/auth-context.d.ts +2 -0
  162. package/dist/lib/auth-context.js +3 -0
  163. package/dist/lib/auth-hook-options.d.ts +8 -0
  164. package/dist/lib/auth-hook-options.js +1 -0
  165. package/dist/lib/provider-icons.d.ts +13 -0
  166. package/dist/lib/provider-icons.js +48 -0
  167. package/dist/lib/utils.d.ts +2 -0
  168. package/dist/lib/utils.js +5 -0
  169. package/package.json +64 -0
  170. package/src/components/auth/auth-provider.tsx +82 -0
  171. package/src/components/email/email-changed.tsx +381 -0
  172. package/src/components/email/email-localization.tsx +33 -0
  173. package/src/components/email/email-styles.tsx +175 -0
  174. package/src/components/email/email-verification.tsx +350 -0
  175. package/src/components/email/index.ts +9 -0
  176. package/src/components/email/magic-link.tsx +349 -0
  177. package/src/components/email/new-device.tsx +452 -0
  178. package/src/components/email/otp-email.tsx +321 -0
  179. package/src/components/email/password-changed.tsx +380 -0
  180. package/src/components/email/reset-password.tsx +343 -0
  181. package/src/components/icons/apple.tsx +24 -0
  182. package/src/components/icons/atlassian.tsx +43 -0
  183. package/src/components/icons/cognito.tsx +25 -0
  184. package/src/components/icons/discord.tsx +24 -0
  185. package/src/components/icons/dropbox.tsx +24 -0
  186. package/src/components/icons/facebook.tsx +30 -0
  187. package/src/components/icons/figma.tsx +43 -0
  188. package/src/components/icons/github.tsx +26 -0
  189. package/src/components/icons/gitlab.tsx +36 -0
  190. package/src/components/icons/google.tsx +42 -0
  191. package/src/components/icons/huggingface.tsx +44 -0
  192. package/src/components/icons/index.ts +35 -0
  193. package/src/components/icons/kakao.tsx +26 -0
  194. package/src/components/icons/kick.tsx +30 -0
  195. package/src/components/icons/line.tsx +24 -0
  196. package/src/components/icons/linear.tsx +24 -0
  197. package/src/components/icons/linkedin.tsx +28 -0
  198. package/src/components/icons/microsoft.tsx +24 -0
  199. package/src/components/icons/naver.tsx +24 -0
  200. package/src/components/icons/notion.tsx +25 -0
  201. package/src/components/icons/paybin.tsx +37 -0
  202. package/src/components/icons/paypal.tsx +36 -0
  203. package/src/components/icons/polar.tsx +31 -0
  204. package/src/components/icons/railway.tsx +24 -0
  205. package/src/components/icons/reddit.tsx +25 -0
  206. package/src/components/icons/roblox.tsx +28 -0
  207. package/src/components/icons/salesforce.tsx +24 -0
  208. package/src/components/icons/slack.tsx +36 -0
  209. package/src/components/icons/spotify.tsx +24 -0
  210. package/src/components/icons/tiktok.tsx +32 -0
  211. package/src/components/icons/twitch.tsx +50 -0
  212. package/src/components/icons/vercel.tsx +21 -0
  213. package/src/components/icons/vk.tsx +24 -0
  214. package/src/components/icons/wechat.tsx +24 -0
  215. package/src/components/icons/x.tsx +24 -0
  216. package/src/components/icons/zoom.tsx +48 -0
  217. package/src/components/settings/account/theme-preview.tsx +271 -0
  218. package/src/core.ts +1 -0
  219. package/src/hooks/auth/use-auth-mutation.ts +56 -0
  220. package/src/hooks/auth/use-auth-query.ts +50 -0
  221. package/src/hooks/auth/use-authenticate.ts +35 -0
  222. package/src/hooks/auth/use-request-password-reset.ts +24 -0
  223. package/src/hooks/auth/use-reset-password.ts +23 -0
  224. package/src/hooks/auth/use-send-verification-email.ts +23 -0
  225. package/src/hooks/auth/use-session.ts +26 -0
  226. package/src/hooks/auth/use-sign-in-email.ts +35 -0
  227. package/src/hooks/auth/use-sign-in-magic-link.ts +23 -0
  228. package/src/hooks/auth/use-sign-in-social.ts +23 -0
  229. package/src/hooks/auth/use-sign-out.ts +34 -0
  230. package/src/hooks/auth/use-sign-up-email.ts +35 -0
  231. package/src/hooks/auth/use-user.ts +21 -0
  232. package/src/hooks/settings/use-account-info.ts +30 -0
  233. package/src/hooks/settings/use-change-email.ts +33 -0
  234. package/src/hooks/settings/use-change-password.ts +21 -0
  235. package/src/hooks/settings/use-delete-user.ts +21 -0
  236. package/src/hooks/settings/use-link-social.ts +19 -0
  237. package/src/hooks/settings/use-list-accounts.ts +27 -0
  238. package/src/hooks/settings/use-list-device-sessions.ts +32 -0
  239. package/src/hooks/settings/use-list-sessions.ts +26 -0
  240. package/src/hooks/settings/use-revoke-multi-session.ts +30 -0
  241. package/src/hooks/settings/use-revoke-session.ts +31 -0
  242. package/src/hooks/settings/use-set-active-session.ts +46 -0
  243. package/src/hooks/settings/use-unlink-account.ts +31 -0
  244. package/src/hooks/settings/use-update-user.ts +44 -0
  245. package/src/index.ts +34 -0
  246. package/src/lib/auth-client.ts +37 -0
  247. package/src/lib/auth-config.ts +48 -0
  248. package/src/lib/auth-context.ts +6 -0
  249. package/src/lib/provider-icons.ts +90 -0
  250. package/src/lib/utils.ts +6 -0
@@ -0,0 +1,75 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Body, Button, Container, Head, Heading, Hr, Html, Img, Link, Preview, pixelBasedPreset, Section, Tailwind, Text } from "@react-email/components";
3
+ import { cn } from "../../lib/utils";
4
+ import { EmailStyles } from "./email-styles";
5
+ const emailVerificationEmailLocalization = {
6
+ VERIFY_YOUR_EMAIL_ADDRESS: "Verify your email address",
7
+ LOGO: "Logo",
8
+ CLICK_BUTTON_TO_VERIFY_EMAIL: "Click the button below to verify your email address {emailAddress} for your {appName} account.",
9
+ VERIFY_EMAIL_ADDRESS: "Verify email address",
10
+ OR_COPY_AND_PASTE_URL: "Or copy and paste this URL into your browser:",
11
+ THIS_LINK_EXPIRES_IN_MINUTES: "This link expires in {expirationMinutes} minutes.",
12
+ EMAIL_SENT_BY: "Email sent by {appName}.",
13
+ IF_YOU_DIDNT_REQUEST_THIS_EMAIL: "If you didn't request this email, you can safely ignore it. Someone else might have typed your email address by mistake.",
14
+ POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
15
+ };
16
+ /**
17
+ * Email template component that sends email verification links to users.
18
+ *
19
+ * This email includes:
20
+ * - Verification button and fallback URL
21
+ * - Expiration time information
22
+ * - Security notice for unauthorized requests
23
+ * - Customizable branding and styling
24
+ * - Support for light/dark mode themes
25
+ *
26
+ * @example
27
+ * ```tsx
28
+ * <EmailVerificationEmail
29
+ * url="https://example.com/verify?token=abc123"
30
+ * email="user@example.com"
31
+ * appName="My App"
32
+ * expirationMinutes={60}
33
+ * logoURL="https://example.com/logo.png"
34
+ * darkMode={true}
35
+ * />
36
+ * ```
37
+ */
38
+ export const EmailVerificationEmail = ({ url, email, appName, expirationMinutes = 60, logoURL, colors, classNames, darkMode = true, poweredBy, head, ...props }) => {
39
+ const localization = {
40
+ ...EmailVerificationEmail.localization,
41
+ ...props.localization
42
+ };
43
+ const previewText = localization.VERIFY_YOUR_EMAIL_ADDRESS;
44
+ return (_jsxs(Html, { children: [_jsxs(Head, { children: [_jsx("meta", { content: "light dark", name: "color-scheme" }), _jsx("meta", { content: "light dark", name: "supported-color-schemes" }), _jsx(EmailStyles, { colors: colors, darkMode: darkMode }), head] }), _jsx(Preview, { children: previewText }), _jsx(Tailwind, { config: { presets: [pixelBasedPreset] }, children: _jsx(Body, { className: cn("bg-background font-sans", classNames?.body), children: _jsx(Container, { className: cn("mx-auto my-auto max-w-xl px-2 py-10", classNames?.container), children: _jsxs(Section, { className: cn("bg-card text-card-foreground rounded-none border border-border p-8", classNames?.card), children: [logoURL &&
45
+ (typeof logoURL === "string" ? (_jsx(Img, { src: logoURL, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("mx-auto mb-8", classNames?.logo) })) : (_jsxs(_Fragment, { children: [_jsx(Img, { src: logoURL.light, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("mx-auto mb-8 logo-light", classNames?.logo) }), _jsx(Img, { src: logoURL.dark, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("hidden mx-auto mb-8 logo-dark", classNames?.logo) })] }))), _jsx(Heading, { className: cn("m-0 mb-5 text-2xl font-semibold", classNames?.title), children: localization.VERIFY_EMAIL_ADDRESS }), _jsx(Text, { className: cn("text-sm font-normal", classNames?.content), children: (() => {
46
+ const textWithAppName = localization.CLICK_BUTTON_TO_VERIFY_EMAIL.replace("{appName}", appName || "")
47
+ .replace(/\s{2,}/g, " ")
48
+ .replace(" .", ".");
49
+ const [beforeEmailAddress, afterEmailAddress] = textWithAppName.split("{emailAddress}");
50
+ return email ? (_jsxs(_Fragment, { children: [beforeEmailAddress, _jsx(Link, { href: `mailto:${email}`, className: "text-primary font-medium", children: email }), afterEmailAddress] })) : (textWithAppName
51
+ .replace("{emailAddress}", "")
52
+ .replace(/\s{2,}/g, " ")
53
+ .replace(" .", "."));
54
+ })() }), _jsx(Section, { className: "my-6", children: _jsx(Button, { href: url, className: cn("inline-block whitespace-nowrap rounded-none text-sm font-medium py-2.5 px-6 bg-primary text-primary-foreground no-underline", classNames?.button), children: localization.VERIFY_EMAIL_ADDRESS }) }), _jsx(Text, { className: cn("mb-3 text-xs text-muted-foreground", classNames?.description), children: localization.OR_COPY_AND_PASTE_URL }), _jsx(Link, { className: cn("break-all text-xs text-primary", classNames?.link), href: url, children: url }), _jsx(Hr, { className: cn("my-6 w-full border border-solid border-border", classNames?.separator) }), expirationMinutes || appName ? (_jsxs(Text, { className: cn("mb-3 text-xs text-muted-foreground", classNames?.description), children: [expirationMinutes
55
+ ? localization.THIS_LINK_EXPIRES_IN_MINUTES.replace("{expirationMinutes}", expirationMinutes.toString())
56
+ : null, appName && (_jsxs(_Fragment, { children: [expirationMinutes ? " " : "", localization.EMAIL_SENT_BY.replace("{appName}", appName)] }))] })) : null, _jsx(Text, { className: cn("mt-3 text-xs text-muted-foreground", classNames?.description), children: localization.IF_YOU_DIDNT_REQUEST_THIS_EMAIL }), poweredBy && (_jsx(Text, { className: cn("mt-4 mb-0 text-center text-[11px] text-muted-foreground", classNames?.poweredBy), children: (() => {
57
+ const [beforeBetterAuth, afterBetterAuth] = localization.POWERED_BY_BETTER_AUTH.split("{betterAuth}");
58
+ return (_jsxs(_Fragment, { children: [beforeBetterAuth, _jsx(Link, { href: "https://better-auth.com", className: cn("text-primary underline", classNames?.link), children: "better-auth" }), afterBetterAuth] }));
59
+ })() }))] }) }) }) })] }));
60
+ };
61
+ /**
62
+ * Default localization strings for the email verification template.
63
+ * Can be overridden via the `localization` prop.
64
+ */
65
+ EmailVerificationEmail.localization = emailVerificationEmailLocalization;
66
+ /**
67
+ * Example props for previewing the email template in development.
68
+ */
69
+ EmailVerificationEmail.PreviewProps = {
70
+ url: "https://better-auth-ui.com/auth/verify-email?token=example-token",
71
+ appName: "Better Auth",
72
+ email: "m@example.com",
73
+ darkMode: true
74
+ };
75
+ export default EmailVerificationEmail;
@@ -0,0 +1,9 @@
1
+ export * from "./email-changed";
2
+ export * from "./email-localization";
3
+ export * from "./email-styles";
4
+ export * from "./email-verification";
5
+ export * from "./magic-link";
6
+ export * from "./new-device";
7
+ export * from "./otp-email";
8
+ export * from "./password-changed";
9
+ export * from "./reset-password";
@@ -0,0 +1,9 @@
1
+ export * from "./email-changed";
2
+ export * from "./email-localization";
3
+ export * from "./email-styles";
4
+ export * from "./email-verification";
5
+ export * from "./magic-link";
6
+ export * from "./new-device";
7
+ export * from "./otp-email";
8
+ export * from "./password-changed";
9
+ export * from "./reset-password";
@@ -0,0 +1,93 @@
1
+ import type { ReactNode } from "react";
2
+ import { type EmailClassNames, type EmailColors } from "./email-styles";
3
+ declare const magicLinkEmailLocalization: {
4
+ SIGN_IN_TO_APP_NAME: string;
5
+ SIGN_IN_TO_YOUR_ACCOUNT: string;
6
+ YOUR_ACCOUNT: string;
7
+ LOGO: string;
8
+ CLICK_BUTTON_TO_SIGN_IN: string;
9
+ OR_COPY_AND_PASTE_URL: string;
10
+ THIS_LINK_EXPIRES_IN_MINUTES: string;
11
+ EMAIL_SENT_BY: string;
12
+ IF_YOU_DIDNT_REQUEST_THIS_EMAIL: string;
13
+ POWERED_BY_BETTER_AUTH: string;
14
+ };
15
+ /**
16
+ * Localization strings for the MagicLinkEmail component.
17
+ *
18
+ * Contains all text content used in the magic link authentication email template.
19
+ */
20
+ export type MagicLinkEmailLocalization = typeof magicLinkEmailLocalization;
21
+ /**
22
+ * Props for the MagicLinkEmail component.
23
+ */
24
+ export interface MagicLinkEmailProps {
25
+ /** Magic link URL for passwordless authentication */
26
+ url: string;
27
+ /** Email address of the user signing in */
28
+ email?: string;
29
+ /** Name of the application sending the email */
30
+ appName?: string;
31
+ /** Number of minutes until the magic link expires */
32
+ expirationMinutes?: number;
33
+ /** Logo URL(s) - a single string or light/dark variants. If omitted, no logo is shown. */
34
+ logoURL?: string | {
35
+ light: string;
36
+ dark: string;
37
+ };
38
+ /** Custom CSS class names for styling specific parts of the email */
39
+ classNames?: EmailClassNames;
40
+ /** Custom color scheme for light and dark modes */
41
+ colors?: EmailColors;
42
+ /** Whether to show the "Powered by better-auth" footer */
43
+ poweredBy?: boolean;
44
+ /** Whether to enable dark mode support */
45
+ darkMode?: boolean;
46
+ /** Additional React nodes to inject into the email head */
47
+ head?: ReactNode;
48
+ /**
49
+ * Localization overrides for customizing email text
50
+ * @remarks `MagicLinkEmailLocalization`
51
+ */
52
+ localization?: Partial<MagicLinkEmailLocalization>;
53
+ }
54
+ /**
55
+ * Email template component that sends magic link authentication emails for passwordless sign-in.
56
+ *
57
+ * This email includes:
58
+ * - Sign-in button with magic link
59
+ * - Fallback URL for manual copy/paste
60
+ * - Expiration time information
61
+ * - Security notice for unauthorized requests
62
+ * - Customizable branding and styling
63
+ * - Support for light/dark mode themes
64
+ *
65
+ * @example
66
+ * ```tsx
67
+ * <MagicLinkEmail
68
+ * url="https://example.com/auth/verify?token=abc123"
69
+ * email="user@example.com"
70
+ * appName="My App"
71
+ * expirationMinutes={5}
72
+ * logoURL="https://example.com/logo.png"
73
+ * darkMode={true}
74
+ * />
75
+ * ```
76
+ */
77
+ export declare const MagicLinkEmail: {
78
+ ({ url, email, appName, expirationMinutes, logoURL, colors, classNames, darkMode, poweredBy, head, ...props }: MagicLinkEmailProps): import("react/jsx-runtime").JSX.Element;
79
+ localization: {
80
+ SIGN_IN_TO_APP_NAME: string;
81
+ SIGN_IN_TO_YOUR_ACCOUNT: string;
82
+ YOUR_ACCOUNT: string;
83
+ LOGO: string;
84
+ CLICK_BUTTON_TO_SIGN_IN: string;
85
+ OR_COPY_AND_PASTE_URL: string;
86
+ THIS_LINK_EXPIRES_IN_MINUTES: string;
87
+ EMAIL_SENT_BY: string;
88
+ IF_YOU_DIDNT_REQUEST_THIS_EMAIL: string;
89
+ POWERED_BY_BETTER_AUTH: string;
90
+ };
91
+ PreviewProps: MagicLinkEmailProps;
92
+ };
93
+ export default MagicLinkEmail;
@@ -0,0 +1,72 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Body, Button, Container, Head, Heading, Hr, Html, Img, Link, Preview, pixelBasedPreset, Section, Tailwind, Text } from "@react-email/components";
3
+ import { cn } from "../../lib/utils";
4
+ import { EmailStyles } from "./email-styles";
5
+ const magicLinkEmailLocalization = {
6
+ SIGN_IN_TO_APP_NAME: "Sign in to {appName}",
7
+ SIGN_IN_TO_YOUR_ACCOUNT: "Sign in to your account",
8
+ YOUR_ACCOUNT: "your account",
9
+ LOGO: "Logo",
10
+ CLICK_BUTTON_TO_SIGN_IN: "Click the button below to sign in to your account {emailAddress}.",
11
+ OR_COPY_AND_PASTE_URL: "Or copy and paste this URL into your browser:",
12
+ THIS_LINK_EXPIRES_IN_MINUTES: "This link expires in {expirationMinutes} minutes.",
13
+ EMAIL_SENT_BY: "Email sent by {appName}.",
14
+ IF_YOU_DIDNT_REQUEST_THIS_EMAIL: "If you didn't request this email, you can safely ignore it. Someone else might have typed your email address by mistake.",
15
+ POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
16
+ };
17
+ /**
18
+ * Email template component that sends magic link authentication emails for passwordless sign-in.
19
+ *
20
+ * This email includes:
21
+ * - Sign-in button with magic link
22
+ * - Fallback URL for manual copy/paste
23
+ * - Expiration time information
24
+ * - Security notice for unauthorized requests
25
+ * - Customizable branding and styling
26
+ * - Support for light/dark mode themes
27
+ *
28
+ * @example
29
+ * ```tsx
30
+ * <MagicLinkEmail
31
+ * url="https://example.com/auth/verify?token=abc123"
32
+ * email="user@example.com"
33
+ * appName="My App"
34
+ * expirationMinutes={5}
35
+ * logoURL="https://example.com/logo.png"
36
+ * darkMode={true}
37
+ * />
38
+ * ```
39
+ */
40
+ export const MagicLinkEmail = ({ url, email, appName, expirationMinutes = 5, logoURL, colors, classNames, darkMode = true, poweredBy, head, ...props }) => {
41
+ const localization = {
42
+ ...MagicLinkEmail.localization,
43
+ ...props.localization
44
+ };
45
+ const previewText = appName
46
+ ? localization.SIGN_IN_TO_APP_NAME.replace("{appName}", appName)
47
+ : localization.SIGN_IN_TO_YOUR_ACCOUNT;
48
+ return (_jsxs(Html, { children: [_jsxs(Head, { children: [_jsx("meta", { content: "light dark", name: "color-scheme" }), _jsx("meta", { content: "light dark", name: "supported-color-schemes" }), _jsx(EmailStyles, { colors: colors, darkMode: darkMode }), head] }), _jsx(Preview, { children: previewText }), _jsx(Tailwind, { config: { presets: [pixelBasedPreset] }, children: _jsx(Body, { className: cn("bg-background font-sans", classNames?.body), children: _jsx(Container, { className: cn("mx-auto my-auto max-w-xl px-2 py-10", classNames?.container), children: _jsxs(Section, { className: cn("bg-card text-card-foreground rounded-none border border-border p-8", classNames?.card), children: [logoURL &&
49
+ (typeof logoURL === "string" ? (_jsx(Img, { src: logoURL, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("mx-auto mb-8", classNames?.logo) })) : (_jsxs(_Fragment, { children: [_jsx(Img, { src: logoURL.light, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("mx-auto mb-8 logo-light", classNames?.logo) }), _jsx(Img, { src: logoURL.dark, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("hidden mx-auto mb-8 logo-dark", classNames?.logo) })] }))), _jsx(Heading, { className: cn("m-0 mb-5 text-2xl font-semibold", classNames?.title), children: appName
50
+ ? localization.SIGN_IN_TO_APP_NAME.replace("{appName}", appName)
51
+ : localization.SIGN_IN_TO_YOUR_ACCOUNT }), _jsx(Text, { className: cn("m-0 text-sm font-normal", classNames?.content), children: (() => {
52
+ const [beforeEmailAddress, afterEmailAddress] = localization.CLICK_BUTTON_TO_SIGN_IN.split("{emailAddress}");
53
+ return email ? (_jsxs(_Fragment, { children: [beforeEmailAddress, _jsx(Link, { href: `mailto:${email}`, className: "text-primary font-medium", children: email }), afterEmailAddress] })) : (localization.CLICK_BUTTON_TO_SIGN_IN.replace("{emailAddress}", "")
54
+ .replace(/\s{2,}/g, " ")
55
+ .replace(" .", "."));
56
+ })() }), _jsx(Section, { className: "my-6", children: _jsx(Button, { href: url, className: cn("inline-block whitespace-nowrap rounded-none text-sm font-medium py-2.5 px-6 bg-primary text-primary-foreground no-underline", classNames?.button), children: appName
57
+ ? localization.SIGN_IN_TO_APP_NAME.replace("{appName}", appName)
58
+ : localization.SIGN_IN_TO_YOUR_ACCOUNT }) }), _jsx(Text, { className: cn("m-0 mb-3 text-xs text-muted-foreground", classNames?.description), children: localization.OR_COPY_AND_PASTE_URL }), _jsx(Link, { className: cn("break-all text-xs text-primary", classNames?.link), href: url, children: url }), _jsx(Hr, { className: cn("my-6 w-full border border-solid border-border", classNames?.separator) }), expirationMinutes || appName ? (_jsxs(Text, { className: cn("m-0 mb-3 text-xs text-muted-foreground", classNames?.description), children: [expirationMinutes
59
+ ? localization.THIS_LINK_EXPIRES_IN_MINUTES.replace("{expirationMinutes}", expirationMinutes.toString())
60
+ : null, appName && (_jsxs(_Fragment, { children: [expirationMinutes ? " " : "", localization.EMAIL_SENT_BY.replace("{appName}", appName)] }))] })) : null, _jsx(Text, { className: cn("m-0 text-xs text-muted-foreground", classNames?.description), children: localization.IF_YOU_DIDNT_REQUEST_THIS_EMAIL }), poweredBy && (_jsx(Text, { className: cn("m-0 mt-4 text-center text-[11px] text-muted-foreground", classNames?.poweredBy), children: (() => {
61
+ const [beforeBetterAuth, afterBetterAuth] = localization.POWERED_BY_BETTER_AUTH.split("{betterAuth}");
62
+ return (_jsxs(_Fragment, { children: [beforeBetterAuth, _jsx(Link, { href: "https://better-auth.com", className: cn("text-primary underline", classNames?.link), children: "better-auth" }), afterBetterAuth] }));
63
+ })() }))] }) }) }) })] }));
64
+ };
65
+ MagicLinkEmail.localization = magicLinkEmailLocalization;
66
+ MagicLinkEmail.PreviewProps = {
67
+ url: "https://better-auth-ui.com/auth/verify?token=example-token",
68
+ email: "m@example.com",
69
+ appName: "Better Auth",
70
+ darkMode: true
71
+ };
72
+ export default MagicLinkEmail;
@@ -0,0 +1,123 @@
1
+ import type { ReactNode } from "react";
2
+ import { type EmailClassNames, type EmailColors } from "./email-styles";
3
+ /**
4
+ * Device information displayed in the new device email notification.
5
+ */
6
+ export interface DeviceInfo {
7
+ /** Browser name and version */
8
+ browser?: string;
9
+ /** Operating system name and version */
10
+ os?: string;
11
+ /** Geographic location of the sign-in */
12
+ location?: string;
13
+ /** IP address of the device */
14
+ ipAddress?: string;
15
+ /** Timestamp of the sign-in event */
16
+ timestamp?: string;
17
+ }
18
+ declare const newDeviceEmailLocalization: {
19
+ NEW_SIGN_IN_DETECTED: string;
20
+ LOGO: string;
21
+ NEW_SIGN_IN_TO_YOUR_ACCOUNT: string;
22
+ DEVICE_DETAILS: string;
23
+ BROWSER: string;
24
+ OPERATING_SYSTEM: string;
25
+ LOCATION: string;
26
+ IP_ADDRESS: string;
27
+ TIME: string;
28
+ IF_THIS_WAS_YOU: string;
29
+ SECURE_MY_ACCOUNT: string;
30
+ EMAIL_SENT_BY: string;
31
+ IF_YOU_DIDNT_SIGN_IN: string;
32
+ POWERED_BY_BETTER_AUTH: string;
33
+ };
34
+ /**
35
+ * Localization strings for the NewDeviceEmail component.
36
+ *
37
+ * Contains all text content used in the new device detection email template.
38
+ */
39
+ export type NewDeviceEmailLocalization = typeof newDeviceEmailLocalization;
40
+ /**
41
+ * Props for the NewDeviceEmail component.
42
+ */
43
+ export interface NewDeviceEmailProps {
44
+ /** Email address of the user account */
45
+ userEmail?: string;
46
+ /** Information about the device that signed in */
47
+ deviceInfo?: DeviceInfo;
48
+ /** URL to secure the account if unauthorized access is suspected */
49
+ secureAccountLink?: string;
50
+ /** Name of the application sending the email */
51
+ appName?: string;
52
+ /** Support email address for security concerns */
53
+ supportEmail?: string;
54
+ /** Logo URL(s) - a single string or light/dark variants. If omitted, no logo is shown. */
55
+ logoURL?: string | {
56
+ light: string;
57
+ dark: string;
58
+ };
59
+ /** Custom CSS class names for styling specific parts of the email */
60
+ classNames?: EmailClassNames;
61
+ /** Custom color scheme for light and dark modes */
62
+ colors?: EmailColors;
63
+ /** Whether to show the "Powered by better-auth" footer */
64
+ poweredBy?: boolean;
65
+ /** Whether to enable dark mode support */
66
+ darkMode?: boolean;
67
+ /** Additional React nodes to inject into the email head */
68
+ head?: ReactNode;
69
+ /**
70
+ * Localization overrides for customizing email text
71
+ * @remarks `NewDeviceEmailLocalization`
72
+ */
73
+ localization?: Partial<NewDeviceEmailLocalization>;
74
+ }
75
+ /**
76
+ * Email template component that notifies users when a new device signs into their account.
77
+ *
78
+ * This email includes:
79
+ * - Device information display (browser, OS, location, IP, timestamp)
80
+ * - Secure account action button
81
+ * - Security warnings and support contact information
82
+ * - Customizable branding and styling
83
+ * - Support for light/dark mode themes
84
+ *
85
+ * @example
86
+ * ```tsx
87
+ * <NewDeviceEmail
88
+ * userEmail="user@example.com"
89
+ * deviceInfo={{
90
+ * browser: "Chrome on macOS",
91
+ * os: "macOS 14.0",
92
+ * location: "San Francisco, CA",
93
+ * ipAddress: "192.168.1.1",
94
+ * timestamp: "February 10, 2025 at 4:20 PM UTC"
95
+ * }}
96
+ * secureAccountLink="https://example.com/secure-account"
97
+ * appName="My App"
98
+ * supportEmail="support@example.com"
99
+ * darkMode={true}
100
+ * />
101
+ * ```
102
+ */
103
+ export declare const NewDeviceEmail: {
104
+ ({ userEmail, deviceInfo, secureAccountLink, appName, supportEmail, logoURL, colors, classNames, darkMode, poweredBy, head, ...props }: NewDeviceEmailProps): import("react/jsx-runtime").JSX.Element;
105
+ localization: {
106
+ NEW_SIGN_IN_DETECTED: string;
107
+ LOGO: string;
108
+ NEW_SIGN_IN_TO_YOUR_ACCOUNT: string;
109
+ DEVICE_DETAILS: string;
110
+ BROWSER: string;
111
+ OPERATING_SYSTEM: string;
112
+ LOCATION: string;
113
+ IP_ADDRESS: string;
114
+ TIME: string;
115
+ IF_THIS_WAS_YOU: string;
116
+ SECURE_MY_ACCOUNT: string;
117
+ EMAIL_SENT_BY: string;
118
+ IF_YOU_DIDNT_SIGN_IN: string;
119
+ POWERED_BY_BETTER_AUTH: string;
120
+ };
121
+ PreviewProps: NewDeviceEmailProps;
122
+ };
123
+ export default NewDeviceEmail;
@@ -0,0 +1,90 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Body, Button, Container, Head, Heading, Hr, Html, Img, Link, Preview, pixelBasedPreset, Section, Tailwind, Text } from "@react-email/components";
3
+ import { cn } from "../../lib/utils";
4
+ import { EmailStyles } from "./email-styles";
5
+ const newDeviceEmailLocalization = {
6
+ NEW_SIGN_IN_DETECTED: "New sign-in detected",
7
+ LOGO: "Logo",
8
+ NEW_SIGN_IN_TO_YOUR_ACCOUNT: "We detected a new sign-in to your {appName} account {userEmail} from a device we don't recognize.",
9
+ DEVICE_DETAILS: "Device details",
10
+ BROWSER: "Browser",
11
+ OPERATING_SYSTEM: "Operating System",
12
+ LOCATION: "Location",
13
+ IP_ADDRESS: "IP Address",
14
+ TIME: "Time",
15
+ IF_THIS_WAS_YOU: "If this was you, you can safely ignore this email. If you don't recognize this activity, please secure your account immediately.",
16
+ SECURE_MY_ACCOUNT: "Secure my account",
17
+ EMAIL_SENT_BY: "Email sent by {appName}.",
18
+ IF_YOU_DIDNT_SIGN_IN: "If you didn't sign in, please contact support immediately {supportEmail} to secure your account.",
19
+ POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
20
+ };
21
+ /**
22
+ * Email template component that notifies users when a new device signs into their account.
23
+ *
24
+ * This email includes:
25
+ * - Device information display (browser, OS, location, IP, timestamp)
26
+ * - Secure account action button
27
+ * - Security warnings and support contact information
28
+ * - Customizable branding and styling
29
+ * - Support for light/dark mode themes
30
+ *
31
+ * @example
32
+ * ```tsx
33
+ * <NewDeviceEmail
34
+ * userEmail="user@example.com"
35
+ * deviceInfo={{
36
+ * browser: "Chrome on macOS",
37
+ * os: "macOS 14.0",
38
+ * location: "San Francisco, CA",
39
+ * ipAddress: "192.168.1.1",
40
+ * timestamp: "February 10, 2025 at 4:20 PM UTC"
41
+ * }}
42
+ * secureAccountLink="https://example.com/secure-account"
43
+ * appName="My App"
44
+ * supportEmail="support@example.com"
45
+ * darkMode={true}
46
+ * />
47
+ * ```
48
+ */
49
+ export const NewDeviceEmail = ({ userEmail, deviceInfo, secureAccountLink, appName, supportEmail, logoURL, colors, classNames, darkMode = true, poweredBy, head, ...props }) => {
50
+ const localization = {
51
+ ...NewDeviceEmail.localization,
52
+ ...props.localization
53
+ };
54
+ const previewText = localization.NEW_SIGN_IN_DETECTED;
55
+ return (_jsxs(Html, { children: [_jsxs(Head, { children: [_jsx("meta", { content: "light dark", name: "color-scheme" }), _jsx("meta", { content: "light dark", name: "supported-color-schemes" }), _jsx(EmailStyles, { colors: colors, darkMode: darkMode }), head] }), _jsx(Preview, { children: previewText }), _jsx(Tailwind, { config: { presets: [pixelBasedPreset] }, children: _jsx(Body, { className: cn("bg-background font-sans", classNames?.body), children: _jsx(Container, { className: cn("mx-auto my-auto max-w-xl px-2 py-10", classNames?.container), children: _jsxs(Section, { className: cn("bg-card text-card-foreground rounded-none border border-border p-8", classNames?.card), children: [logoURL &&
56
+ (typeof logoURL === "string" ? (_jsx(Img, { src: logoURL, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("mx-auto mb-8", classNames?.logo) })) : (_jsxs(_Fragment, { children: [_jsx(Img, { src: logoURL.light, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("mx-auto mb-8 logo-light", classNames?.logo) }), _jsx(Img, { src: logoURL.dark, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("hidden mx-auto mb-8 logo-dark", classNames?.logo) })] }))), _jsx(Heading, { className: cn("m-0 mb-5 text-2xl font-semibold", classNames?.title), children: localization.NEW_SIGN_IN_DETECTED }), _jsx(Text, { className: cn("text-sm font-normal", classNames?.content), children: (() => {
57
+ const textWithAppName = localization.NEW_SIGN_IN_TO_YOUR_ACCOUNT.replace("{appName}", appName || "")
58
+ .replace(/\s{2,}/g, " ")
59
+ .replace(" .", ".");
60
+ const [beforeUserEmail, afterUserEmail] = textWithAppName.split("{userEmail}");
61
+ return userEmail ? (_jsxs(_Fragment, { children: [beforeUserEmail, _jsx(Link, { href: `mailto:${userEmail}`, className: "text-primary font-medium", children: userEmail }), afterUserEmail] })) : (textWithAppName
62
+ .replace("{userEmail}", "")
63
+ .replace(/\s{2,}/g, " ")
64
+ .replace(" .", "."));
65
+ })() }), deviceInfo && (_jsxs(Section, { className: cn("my-6 border border-border bg-muted p-4", classNames?.codeBlock), children: [_jsxs(Text, { className: cn("m-0 mb-3 text-xs text-muted-foreground", classNames?.description), children: [localization.DEVICE_DETAILS, ":"] }), deviceInfo.browser && (_jsxs(Text, { className: cn("m-0 mb-2 text-sm", classNames?.content), children: [_jsxs("span", { className: "font-semibold", children: [localization.BROWSER, ":"] }), " ", deviceInfo.browser] })), deviceInfo.os && (_jsxs(Text, { className: cn("m-0 mb-2 text-sm", classNames?.content), children: [_jsxs("span", { className: "font-semibold", children: [localization.OPERATING_SYSTEM, ":"] }), " ", deviceInfo.os] })), deviceInfo.location && (_jsxs(Text, { className: cn("m-0 mb-2 text-sm", classNames?.content), children: [_jsxs("span", { className: "font-semibold", children: [localization.LOCATION, ":"] }), " ", deviceInfo.location] })), deviceInfo.ipAddress && (_jsxs(Text, { className: cn("m-0 mb-2 text-sm", classNames?.content), children: [_jsxs("span", { className: "font-semibold", children: [localization.IP_ADDRESS, ":"] }), " ", deviceInfo.ipAddress] })), deviceInfo.timestamp && (_jsxs(Text, { className: cn("m-0 text-sm", classNames?.content), children: [_jsxs("span", { className: "font-semibold", children: [localization.TIME, ":"] }), " ", deviceInfo.timestamp] }))] })), _jsx(Text, { className: cn("text-sm font-normal", classNames?.content), children: localization.IF_THIS_WAS_YOU }), secureAccountLink && (_jsx(Section, { className: "mt-6", children: _jsx(Button, { href: secureAccountLink, className: cn("inline-block whitespace-nowrap rounded-none text-sm font-medium py-2.5 px-6 bg-primary text-primary-foreground no-underline", classNames?.button), children: localization.SECURE_MY_ACCOUNT }) })), _jsx(Hr, { className: cn("my-6 w-full border border-solid border-border", classNames?.separator) }), appName && (_jsx(Text, { className: cn("mb-3 text-xs text-muted-foreground", classNames?.description), children: localization.EMAIL_SENT_BY.replace("{appName}", appName) })), _jsx(Text, { className: cn("mt-3 text-xs text-muted-foreground", classNames?.description), children: (() => {
66
+ const [beforeSupportEmail, afterSupportEmail] = localization.IF_YOU_DIDNT_SIGN_IN.split("{supportEmail}");
67
+ return supportEmail ? (_jsxs(_Fragment, { children: [beforeSupportEmail, _jsx(Link, { href: `mailto:${supportEmail}`, className: cn("text-primary underline", classNames?.link), children: supportEmail }), afterSupportEmail] })) : (localization.IF_YOU_DIDNT_SIGN_IN.replace("{supportEmail}", "")
68
+ .replace(/\s{2,}/g, " ")
69
+ .replace(" .", "."));
70
+ })() }), poweredBy && (_jsx(Text, { className: cn("mt-4 mb-0 text-center text-[11px] text-muted-foreground", classNames?.poweredBy), children: (() => {
71
+ const [beforeBetterAuth, afterBetterAuth] = localization.POWERED_BY_BETTER_AUTH.split("{betterAuth}");
72
+ return (_jsxs(_Fragment, { children: [beforeBetterAuth, _jsx(Link, { href: "https://better-auth.com", className: cn("text-primary underline", classNames?.link), children: "better-auth" }), afterBetterAuth] }));
73
+ })() }))] }) }) }) })] }));
74
+ };
75
+ NewDeviceEmail.localization = newDeviceEmailLocalization;
76
+ NewDeviceEmail.PreviewProps = {
77
+ userEmail: "m@example.com",
78
+ deviceInfo: {
79
+ browser: "Chrome on macOS",
80
+ os: "macOS 26.2",
81
+ location: "San Francisco, CA, United States",
82
+ ipAddress: "127.0.0.1",
83
+ timestamp: "February 10, 2025 at 4:20 PM UTC"
84
+ },
85
+ secureAccountLink: "https://better-auth-ui.com/auth/secure-account",
86
+ appName: "Better Auth",
87
+ supportEmail: "support@example.com",
88
+ darkMode: true
89
+ };
90
+ export default NewDeviceEmail;
@@ -0,0 +1,88 @@
1
+ import type { ReactNode } from "react";
2
+ import { type EmailClassNames, type EmailColors } from "./email-styles";
3
+ declare const otpEmailLocalization: {
4
+ YOUR_VERIFICATION_CODE_IS_CODE: string;
5
+ LOGO: string;
6
+ VERIFY_YOUR_EMAIL: string;
7
+ WE_NEED_TO_VERIFY_YOUR_EMAIL_ADDRESS: string;
8
+ THIS_CODE_EXPIRES_IN_MINUTES: string;
9
+ EMAIL_SENT_BY: string;
10
+ IF_YOU_DIDNT_REQUEST_THIS_EMAIL: string;
11
+ POWERED_BY_BETTER_AUTH: string;
12
+ };
13
+ /**
14
+ * Localization strings for the OtpEmail component.
15
+ *
16
+ * Contains all text content used in the OTP (One-Time Password) email template.
17
+ */
18
+ export type OtpEmailEmailLocalization = typeof otpEmailLocalization;
19
+ /**
20
+ * Props for the OtpEmail component.
21
+ */
22
+ export interface OtpEmailProps {
23
+ /** The one-time verification code to display */
24
+ verificationCode: string;
25
+ /** Email address being verified */
26
+ email?: string;
27
+ /** Name of the application sending the email */
28
+ appName?: string;
29
+ /** Number of minutes until the verification code expires */
30
+ expirationMinutes?: number;
31
+ /** Logo URL(s) - a single string or light/dark variants. If omitted, no logo is shown. */
32
+ logoURL?: string | {
33
+ light: string;
34
+ dark: string;
35
+ };
36
+ /** Custom CSS class names for styling specific parts of the email */
37
+ classNames?: EmailClassNames;
38
+ /** Custom color scheme for light and dark modes */
39
+ colors?: EmailColors;
40
+ /** Whether to show the "Powered by better-auth" footer */
41
+ poweredBy?: boolean;
42
+ /** Whether to enable dark mode support */
43
+ darkMode?: boolean;
44
+ /** Additional React nodes to inject into the email head */
45
+ head?: ReactNode;
46
+ /**
47
+ * Localization overrides for customizing email text
48
+ * @remarks `OtpEmailEmailLocalization`
49
+ */
50
+ localization?: Partial<OtpEmailEmailLocalization>;
51
+ }
52
+ /**
53
+ * Email template component that sends one-time password (OTP) verification codes to users.
54
+ *
55
+ * This email includes:
56
+ * - Large, prominently displayed verification code
57
+ * - Expiration time information
58
+ * - Security notice for unauthorized requests
59
+ * - Customizable branding and styling
60
+ * - Support for light/dark mode themes
61
+ *
62
+ * @example
63
+ * ```tsx
64
+ * <OtpEmail
65
+ * verificationCode="069420"
66
+ * email="user@example.com"
67
+ * appName="My App"
68
+ * expirationMinutes={10}
69
+ * logoURL="https://example.com/logo.png"
70
+ * darkMode={true}
71
+ * />
72
+ * ```
73
+ */
74
+ export declare const OtpEmail: {
75
+ ({ verificationCode, email, appName, expirationMinutes, logoURL, colors, classNames, darkMode, poweredBy, head, ...props }: OtpEmailProps): import("react/jsx-runtime").JSX.Element;
76
+ localization: {
77
+ YOUR_VERIFICATION_CODE_IS_CODE: string;
78
+ LOGO: string;
79
+ VERIFY_YOUR_EMAIL: string;
80
+ WE_NEED_TO_VERIFY_YOUR_EMAIL_ADDRESS: string;
81
+ THIS_CODE_EXPIRES_IN_MINUTES: string;
82
+ EMAIL_SENT_BY: string;
83
+ IF_YOU_DIDNT_REQUEST_THIS_EMAIL: string;
84
+ POWERED_BY_BETTER_AUTH: string;
85
+ };
86
+ PreviewProps: OtpEmailProps;
87
+ };
88
+ export default OtpEmail;
@@ -0,0 +1,65 @@
1
+ import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
2
+ import { Body, Container, Head, Heading, Hr, Html, Img, Link, Preview, pixelBasedPreset, Section, Tailwind, Text } from "@react-email/components";
3
+ import { cn } from "../../lib/utils";
4
+ import { EmailStyles } from "./email-styles";
5
+ const otpEmailLocalization = {
6
+ YOUR_VERIFICATION_CODE_IS_CODE: "Your verification code is {verificationCode}",
7
+ LOGO: "Logo",
8
+ VERIFY_YOUR_EMAIL: "Verify your email",
9
+ WE_NEED_TO_VERIFY_YOUR_EMAIL_ADDRESS: "We need to verify your email address {email} before you can access your {appName} account. Enter the code below in your open browser window.",
10
+ THIS_CODE_EXPIRES_IN_MINUTES: "This code expires in {expirationMinutes} minutes.",
11
+ EMAIL_SENT_BY: "Email sent by {appName}.",
12
+ IF_YOU_DIDNT_REQUEST_THIS_EMAIL: "If you didn't request this email, you can safely ignore it. Someone else might have typed your email address by mistake.",
13
+ POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
14
+ };
15
+ /**
16
+ * Email template component that sends one-time password (OTP) verification codes to users.
17
+ *
18
+ * This email includes:
19
+ * - Large, prominently displayed verification code
20
+ * - Expiration time information
21
+ * - Security notice for unauthorized requests
22
+ * - Customizable branding and styling
23
+ * - Support for light/dark mode themes
24
+ *
25
+ * @example
26
+ * ```tsx
27
+ * <OtpEmail
28
+ * verificationCode="069420"
29
+ * email="user@example.com"
30
+ * appName="My App"
31
+ * expirationMinutes={10}
32
+ * logoURL="https://example.com/logo.png"
33
+ * darkMode={true}
34
+ * />
35
+ * ```
36
+ */
37
+ export const OtpEmail = ({ verificationCode, email, appName, expirationMinutes = 10, logoURL, colors, classNames, darkMode = true, poweredBy, head, ...props }) => {
38
+ const localization = {
39
+ ...OtpEmail.localization,
40
+ ...props.localization
41
+ };
42
+ const previewText = localization.YOUR_VERIFICATION_CODE_IS_CODE.replace("{verificationCode}", verificationCode);
43
+ return (_jsxs(Html, { children: [_jsxs(Head, { children: [_jsx("meta", { content: "light dark", name: "color-scheme" }), _jsx("meta", { content: "light dark", name: "supported-color-schemes" }), _jsx(EmailStyles, { colors: colors, darkMode: darkMode }), head] }), _jsx(Preview, { children: previewText }), _jsx(Tailwind, { config: { presets: [pixelBasedPreset] }, children: _jsx(Body, { className: cn("bg-background font-sans", classNames?.body), children: _jsx(Container, { className: cn("mx-auto my-auto max-w-xl px-2 py-10", classNames?.container), children: _jsxs(Section, { className: cn("bg-card text-card-foreground rounded-none border border-border p-8", classNames?.card), children: [logoURL &&
44
+ (typeof logoURL === "string" ? (_jsx(Img, { src: logoURL, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("mx-auto mb-8", classNames?.logo) })) : (_jsxs(_Fragment, { children: [_jsx(Img, { src: logoURL.light, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("mx-auto mb-8 logo-light", classNames?.logo) }), _jsx(Img, { src: logoURL.dark, width: 48, height: 48, alt: appName || localization.LOGO, className: cn("hidden mx-auto mb-8 logo-dark", classNames?.logo) })] }))), _jsx(Heading, { className: cn("mb-5 text-2xl font-semibold", classNames?.title), children: localization.VERIFY_YOUR_EMAIL }), _jsx(Text, { className: cn("text-sm font-normal", classNames?.content), children: (() => {
45
+ const textWithAppName = localization.WE_NEED_TO_VERIFY_YOUR_EMAIL_ADDRESS.replace("{appName}", appName || "")
46
+ .replace(/\s{2,}/g, " ")
47
+ .replace(" .", ".");
48
+ const [beforeEmail, afterEmail] = textWithAppName.split("{email}");
49
+ return email ? (_jsxs(_Fragment, { children: [beforeEmail, _jsx(Link, { href: `mailto:${email}`, className: "text-primary font-medium", children: email }), afterEmail] })) : (textWithAppName
50
+ .replace("{email}", "")
51
+ .replace(/\s{2,}/g, " ")
52
+ .replace(" .", "."));
53
+ })() }), _jsx(Section, { className: cn("my-6 border border-border bg-muted p-6", classNames?.codeBlock), children: _jsx(Text, { className: cn("m-0 text-center text-4xl font-semibold tracking-widest", classNames?.title), children: verificationCode }) }), _jsx(Hr, { className: cn("my-6 w-full border border-solid border-border", classNames?.separator) }), _jsxs(Text, { className: cn("mb-3 text-xs text-muted-foreground", classNames?.description), children: [localization.THIS_CODE_EXPIRES_IN_MINUTES.replace("{expirationMinutes}", expirationMinutes.toString()), appName && (_jsxs(_Fragment, { children: [" ", localization.EMAIL_SENT_BY.replace("{appName}", appName)] }))] }), _jsx(Text, { className: cn("mt-3 text-xs text-muted-foreground", classNames?.description), children: localization.IF_YOU_DIDNT_REQUEST_THIS_EMAIL }), poweredBy && (_jsx(Text, { className: cn("mt-4 mb-0 text-center text-[11px] text-muted-foreground", classNames?.poweredBy), children: (() => {
54
+ const [beforeBetterAuth, afterBetterAuth] = localization.POWERED_BY_BETTER_AUTH.split("{betterAuth}");
55
+ return (_jsxs(_Fragment, { children: [beforeBetterAuth, _jsx(Link, { href: "https://better-auth.com", className: cn("text-primary underline", classNames?.link), children: "better-auth" }), afterBetterAuth] }));
56
+ })() }))] }) }) }) })] }));
57
+ };
58
+ OtpEmail.localization = otpEmailLocalization;
59
+ OtpEmail.PreviewProps = {
60
+ verificationCode: "069420",
61
+ email: "m@example.com",
62
+ appName: "Better Auth",
63
+ darkMode: true
64
+ };
65
+ export default OtpEmail;