@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,321 @@
1
+ import {
2
+ Body,
3
+ Container,
4
+ Head,
5
+ Heading,
6
+ Hr,
7
+ Html,
8
+ Img,
9
+ Link,
10
+ Preview,
11
+ pixelBasedPreset,
12
+ Section,
13
+ Tailwind,
14
+ Text
15
+ } from "@react-email/components"
16
+ import type { ReactNode } from "react"
17
+
18
+ import { cn } from "../../lib/utils"
19
+ import {
20
+ type EmailClassNames,
21
+ type EmailColors,
22
+ EmailStyles
23
+ } from "./email-styles"
24
+
25
+ const otpEmailLocalization = {
26
+ YOUR_VERIFICATION_CODE_IS_CODE:
27
+ "Your verification code is {verificationCode}",
28
+ LOGO: "Logo",
29
+ VERIFY_YOUR_EMAIL: "Verify your email",
30
+ WE_NEED_TO_VERIFY_YOUR_EMAIL_ADDRESS:
31
+ "We need to verify your email address {email} before you can access your {appName} account. Enter the code below in your open browser window.",
32
+ THIS_CODE_EXPIRES_IN_MINUTES:
33
+ "This code expires in {expirationMinutes} minutes.",
34
+ EMAIL_SENT_BY: "Email sent by {appName}.",
35
+ IF_YOU_DIDNT_REQUEST_THIS_EMAIL:
36
+ "If you didn't request this email, you can safely ignore it. Someone else might have typed your email address by mistake.",
37
+ POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
38
+ }
39
+
40
+ /**
41
+ * Localization strings for the OtpEmail component.
42
+ *
43
+ * Contains all text content used in the OTP (One-Time Password) email template.
44
+ */
45
+ export type OtpEmailEmailLocalization = typeof otpEmailLocalization
46
+
47
+ /**
48
+ * Props for the OtpEmail component.
49
+ */
50
+ export interface OtpEmailProps {
51
+ /** The one-time verification code to display */
52
+ verificationCode: string
53
+ /** Email address being verified */
54
+ email?: string
55
+ /** Name of the application sending the email */
56
+ appName?: string
57
+ /** Number of minutes until the verification code expires */
58
+ expirationMinutes?: number
59
+ /** Logo URL(s) - a single string or light/dark variants. If omitted, no logo is shown. */
60
+ logoURL?: string | { light: string; dark: string }
61
+ /** Custom CSS class names for styling specific parts of the email */
62
+ classNames?: EmailClassNames
63
+ /** Custom color scheme for light and dark modes */
64
+ colors?: EmailColors
65
+ /** Whether to show the "Powered by better-auth" footer */
66
+ poweredBy?: boolean
67
+ /** Whether to enable dark mode support */
68
+ darkMode?: boolean
69
+ /** Additional React nodes to inject into the email head */
70
+ head?: ReactNode
71
+ /**
72
+ * Localization overrides for customizing email text
73
+ * @remarks `OtpEmailEmailLocalization`
74
+ */
75
+ localization?: Partial<OtpEmailEmailLocalization>
76
+ }
77
+
78
+ /**
79
+ * Email template component that sends one-time password (OTP) verification codes to users.
80
+ *
81
+ * This email includes:
82
+ * - Large, prominently displayed verification code
83
+ * - Expiration time information
84
+ * - Security notice for unauthorized requests
85
+ * - Customizable branding and styling
86
+ * - Support for light/dark mode themes
87
+ *
88
+ * @example
89
+ * ```tsx
90
+ * <OtpEmail
91
+ * verificationCode="069420"
92
+ * email="user@example.com"
93
+ * appName="My App"
94
+ * expirationMinutes={10}
95
+ * logoURL="https://example.com/logo.png"
96
+ * darkMode={true}
97
+ * />
98
+ * ```
99
+ */
100
+ export const OtpEmail = ({
101
+ verificationCode,
102
+ email,
103
+ appName,
104
+ expirationMinutes = 10,
105
+ logoURL,
106
+ colors,
107
+ classNames,
108
+ darkMode = true,
109
+ poweredBy,
110
+ head,
111
+ ...props
112
+ }: OtpEmailProps) => {
113
+ const localization = {
114
+ ...OtpEmail.localization,
115
+ ...props.localization
116
+ }
117
+
118
+ const previewText = localization.YOUR_VERIFICATION_CODE_IS_CODE.replace(
119
+ "{verificationCode}",
120
+ verificationCode
121
+ )
122
+
123
+ return (
124
+ <Html>
125
+ <Head>
126
+ <meta content="light dark" name="color-scheme" />
127
+ <meta content="light dark" name="supported-color-schemes" />
128
+
129
+ <EmailStyles colors={colors} darkMode={darkMode} />
130
+
131
+ {head}
132
+ </Head>
133
+
134
+ <Preview>{previewText}</Preview>
135
+
136
+ <Tailwind config={{ presets: [pixelBasedPreset] }}>
137
+ <Body className={cn("bg-background font-sans", classNames?.body)}>
138
+ <Container
139
+ className={cn(
140
+ "mx-auto my-auto max-w-xl px-2 py-10",
141
+ classNames?.container
142
+ )}
143
+ >
144
+ <Section
145
+ className={cn(
146
+ "bg-card text-card-foreground rounded-none border border-border p-8",
147
+ classNames?.card
148
+ )}
149
+ >
150
+ {logoURL &&
151
+ (typeof logoURL === "string" ? (
152
+ <Img
153
+ src={logoURL}
154
+ width={48}
155
+ height={48}
156
+ alt={appName || localization.LOGO}
157
+ className={cn("mx-auto mb-8", classNames?.logo)}
158
+ />
159
+ ) : (
160
+ <>
161
+ <Img
162
+ src={logoURL.light}
163
+ width={48}
164
+ height={48}
165
+ alt={appName || localization.LOGO}
166
+ className={cn(
167
+ "mx-auto mb-8 logo-light",
168
+ classNames?.logo
169
+ )}
170
+ />
171
+ <Img
172
+ src={logoURL.dark}
173
+ width={48}
174
+ height={48}
175
+ alt={appName || localization.LOGO}
176
+ className={cn(
177
+ "hidden mx-auto mb-8 logo-dark",
178
+ classNames?.logo
179
+ )}
180
+ />
181
+ </>
182
+ ))}
183
+
184
+ <Heading
185
+ className={cn("mb-5 text-2xl font-semibold", classNames?.title)}
186
+ >
187
+ {localization.VERIFY_YOUR_EMAIL}
188
+ </Heading>
189
+
190
+ <Text className={cn("text-sm font-normal", classNames?.content)}>
191
+ {(() => {
192
+ const textWithAppName =
193
+ localization.WE_NEED_TO_VERIFY_YOUR_EMAIL_ADDRESS.replace(
194
+ "{appName}",
195
+ appName || ""
196
+ )
197
+ .replace(/\s{2,}/g, " ")
198
+ .replace(" .", ".")
199
+
200
+ const [beforeEmail, afterEmail] =
201
+ textWithAppName.split("{email}")
202
+
203
+ return email ? (
204
+ <>
205
+ {beforeEmail}
206
+
207
+ <Link
208
+ href={`mailto:${email}`}
209
+ className="text-primary font-medium"
210
+ >
211
+ {email}
212
+ </Link>
213
+
214
+ {afterEmail}
215
+ </>
216
+ ) : (
217
+ textWithAppName
218
+ .replace("{email}", "")
219
+ .replace(/\s{2,}/g, " ")
220
+ .replace(" .", ".")
221
+ )
222
+ })()}
223
+ </Text>
224
+
225
+ <Section
226
+ className={cn(
227
+ "my-6 border border-border bg-muted p-6",
228
+ classNames?.codeBlock
229
+ )}
230
+ >
231
+ <Text
232
+ className={cn(
233
+ "m-0 text-center text-4xl font-semibold tracking-widest",
234
+ classNames?.title
235
+ )}
236
+ >
237
+ {verificationCode}
238
+ </Text>
239
+ </Section>
240
+
241
+ <Hr
242
+ className={cn(
243
+ "my-6 w-full border border-solid border-border",
244
+ classNames?.separator
245
+ )}
246
+ />
247
+
248
+ <Text
249
+ className={cn(
250
+ "mb-3 text-xs text-muted-foreground",
251
+ classNames?.description
252
+ )}
253
+ >
254
+ {localization.THIS_CODE_EXPIRES_IN_MINUTES.replace(
255
+ "{expirationMinutes}",
256
+ expirationMinutes.toString()
257
+ )}
258
+ {appName && (
259
+ <>
260
+ {" "}
261
+ {localization.EMAIL_SENT_BY.replace("{appName}", appName)}
262
+ </>
263
+ )}
264
+ </Text>
265
+
266
+ <Text
267
+ className={cn(
268
+ "mt-3 text-xs text-muted-foreground",
269
+ classNames?.description
270
+ )}
271
+ >
272
+ {localization.IF_YOU_DIDNT_REQUEST_THIS_EMAIL}
273
+ </Text>
274
+
275
+ {poweredBy && (
276
+ <Text
277
+ className={cn(
278
+ "mt-4 mb-0 text-center text-[11px] text-muted-foreground",
279
+ classNames?.poweredBy
280
+ )}
281
+ >
282
+ {(() => {
283
+ const [beforeBetterAuth, afterBetterAuth] =
284
+ localization.POWERED_BY_BETTER_AUTH.split("{betterAuth}")
285
+
286
+ return (
287
+ <>
288
+ {beforeBetterAuth}
289
+ <Link
290
+ href="https://better-auth.com"
291
+ className={cn(
292
+ "text-primary underline",
293
+ classNames?.link
294
+ )}
295
+ >
296
+ better-auth
297
+ </Link>
298
+ {afterBetterAuth}
299
+ </>
300
+ )
301
+ })()}
302
+ </Text>
303
+ )}
304
+ </Section>
305
+ </Container>
306
+ </Body>
307
+ </Tailwind>
308
+ </Html>
309
+ )
310
+ }
311
+
312
+ OtpEmail.localization = otpEmailLocalization
313
+
314
+ OtpEmail.PreviewProps = {
315
+ verificationCode: "069420",
316
+ email: "m@example.com",
317
+ appName: "Better Auth",
318
+ darkMode: true
319
+ } as OtpEmailProps
320
+
321
+ export default OtpEmail
@@ -0,0 +1,380 @@
1
+ import {
2
+ Body,
3
+ Button,
4
+ Container,
5
+ Head,
6
+ Heading,
7
+ Hr,
8
+ Html,
9
+ Img,
10
+ Link,
11
+ Preview,
12
+ pixelBasedPreset,
13
+ Section,
14
+ Tailwind,
15
+ Text
16
+ } from "@react-email/components"
17
+ import type { ReactNode } from "react"
18
+
19
+ import { cn } from "../../lib/utils"
20
+ import {
21
+ type EmailClassNames,
22
+ type EmailColors,
23
+ EmailStyles
24
+ } from "./email-styles"
25
+
26
+ const passwordChangedEmailLocalization = {
27
+ YOUR_PASSWORD_HAS_BEEN_CHANGED: "Your password has been changed",
28
+ LOGO: "Logo",
29
+ PASSWORD_CHANGED_SUCCESSFULLY: "Password changed successfully",
30
+ PASSWORD_FOR_YOUR_ACCOUNT_CHANGED:
31
+ "The password for your {appName} account {userEmail} has been changed successfully.",
32
+ CHANGED_AT: "Changed at",
33
+ IF_YOU_MADE_THIS_CHANGE:
34
+ "If you made this change, you can safely ignore this email. Your account is secure.",
35
+ I_DIDNT_MAKE_THIS_CHANGE: "I didn't make this change",
36
+ EMAIL_SENT_BY: "Email sent by {appName}.",
37
+ IF_YOU_DIDNT_AUTHORIZE_THIS_CHANGE:
38
+ "If you didn't authorize this change, please contact support immediately {supportEmail} to secure your account.",
39
+ POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
40
+ }
41
+
42
+ /**
43
+ * Localization strings for the PasswordChangedEmail component.
44
+ *
45
+ * Contains all text content used in the password changed notification email template.
46
+ */
47
+ export type PasswordChangedEmailLocalization =
48
+ typeof passwordChangedEmailLocalization
49
+
50
+ /**
51
+ * Props for the PasswordChangedEmail component.
52
+ */
53
+ export interface PasswordChangedEmailProps {
54
+ /** Email address of the user account */
55
+ email?: string
56
+ /** Timestamp when the password was changed */
57
+ timestamp?: string
58
+ /** URL to secure the account if unauthorized change occurred */
59
+ secureAccountURL?: string
60
+ /** Name of the application sending the email */
61
+ appName?: string
62
+ /** Support email address for security concerns */
63
+ supportEmail?: string
64
+ /** Logo URL(s) - a single string or light/dark variants. If omitted, no logo is shown. */
65
+ logoURL?: string | { light: string; dark: string }
66
+ /** Custom CSS class names for styling specific parts of the email */
67
+ classNames?: EmailClassNames
68
+ /** Custom color scheme for light and dark modes */
69
+ colors?: EmailColors
70
+ /** Whether to show the "Powered by better-auth" footer */
71
+ poweredBy?: boolean
72
+ /** Whether to enable dark mode support */
73
+ darkMode?: boolean
74
+ /** Additional React nodes to inject into the email head */
75
+ head?: ReactNode
76
+ /**
77
+ * Localization overrides for customizing email text
78
+ * @remarks `PasswordChangedEmailLocalization`
79
+ */
80
+ localization?: Partial<PasswordChangedEmailLocalization>
81
+ }
82
+
83
+ /**
84
+ * Email template component that notifies users when their password has been changed.
85
+ *
86
+ * This email includes:
87
+ * - Password change confirmation message
88
+ * - Timestamp of the change
89
+ * - Secure account action button if unauthorized change occurred
90
+ * - Security warnings and support contact information
91
+ * - Customizable branding and styling
92
+ * - Support for light/dark mode themes
93
+ *
94
+ * @example
95
+ * ```tsx
96
+ * <PasswordChangedEmail
97
+ * email="user@example.com"
98
+ * timestamp="February 10, 2025 at 4:20 PM UTC"
99
+ * secureAccountURL="https://example.com/settings/security"
100
+ * appName="My App"
101
+ * supportEmail="support@example.com"
102
+ * logoURL="https://example.com/logo.png"
103
+ * darkMode={true}
104
+ * />
105
+ * ```
106
+ */
107
+ export const PasswordChangedEmail = ({
108
+ email,
109
+ timestamp,
110
+ secureAccountURL,
111
+ appName,
112
+ supportEmail,
113
+ logoURL,
114
+ colors,
115
+ classNames,
116
+ darkMode = true,
117
+ poweredBy,
118
+ head,
119
+ ...props
120
+ }: PasswordChangedEmailProps) => {
121
+ const localization = {
122
+ ...PasswordChangedEmail.localization,
123
+ ...props.localization
124
+ }
125
+
126
+ const previewText = localization.YOUR_PASSWORD_HAS_BEEN_CHANGED
127
+
128
+ return (
129
+ <Html>
130
+ <Head>
131
+ <meta content="light dark" name="color-scheme" />
132
+ <meta content="light dark" name="supported-color-schemes" />
133
+
134
+ <EmailStyles colors={colors} darkMode={darkMode} />
135
+
136
+ {head}
137
+ </Head>
138
+
139
+ <Preview>{previewText}</Preview>
140
+
141
+ <Tailwind config={{ presets: [pixelBasedPreset] }}>
142
+ <Body className={cn("bg-background font-sans", classNames?.body)}>
143
+ <Container
144
+ className={cn(
145
+ "mx-auto my-auto max-w-xl px-2 py-10",
146
+ classNames?.container
147
+ )}
148
+ >
149
+ <Section
150
+ className={cn(
151
+ "bg-card text-card-foreground rounded-none border border-border p-8",
152
+ classNames?.card
153
+ )}
154
+ >
155
+ {logoURL &&
156
+ (typeof logoURL === "string" ? (
157
+ <Img
158
+ src={logoURL}
159
+ width={48}
160
+ height={48}
161
+ alt={appName || localization.LOGO}
162
+ className={cn("mx-auto mb-8", classNames?.logo)}
163
+ />
164
+ ) : (
165
+ <>
166
+ <Img
167
+ src={logoURL.light}
168
+ width={48}
169
+ height={48}
170
+ alt={appName || localization.LOGO}
171
+ className={cn(
172
+ "mx-auto mb-8 logo-light",
173
+ classNames?.logo
174
+ )}
175
+ />
176
+ <Img
177
+ src={logoURL.dark}
178
+ width={48}
179
+ height={48}
180
+ alt={appName || localization.LOGO}
181
+ className={cn(
182
+ "hidden mx-auto mb-8 logo-dark",
183
+ classNames?.logo
184
+ )}
185
+ />
186
+ </>
187
+ ))}
188
+
189
+ <Heading
190
+ className={cn(
191
+ "m-0 mb-5 text-2xl font-semibold",
192
+ classNames?.title
193
+ )}
194
+ >
195
+ {localization.PASSWORD_CHANGED_SUCCESSFULLY}
196
+ </Heading>
197
+
198
+ <Text className={cn("text-sm font-normal", classNames?.content)}>
199
+ {(() => {
200
+ const textWithAppName =
201
+ localization.PASSWORD_FOR_YOUR_ACCOUNT_CHANGED.replace(
202
+ "{appName}",
203
+ appName || ""
204
+ )
205
+ .replace(/\s{2,}/g, " ")
206
+ .replace(" .", ".")
207
+
208
+ const [beforeUserEmail, afterUserEmail] =
209
+ textWithAppName.split("{userEmail}")
210
+
211
+ return email ? (
212
+ <>
213
+ {beforeUserEmail}
214
+
215
+ <Link
216
+ href={`mailto:${email}`}
217
+ className="text-primary font-medium"
218
+ >
219
+ {email}
220
+ </Link>
221
+
222
+ {afterUserEmail}
223
+ </>
224
+ ) : (
225
+ textWithAppName
226
+ .replace("{userEmail}", "")
227
+ .replace(/\s{2,}/g, " ")
228
+ .replace(" .", ".")
229
+ )
230
+ })()}
231
+ </Text>
232
+
233
+ {timestamp && (
234
+ <Section
235
+ className={cn(
236
+ "my-6 border border-border bg-muted p-4",
237
+ classNames?.codeBlock
238
+ )}
239
+ >
240
+ <Text
241
+ className={cn(
242
+ "m-0 mb-2 text-xs text-muted-foreground",
243
+ classNames?.description
244
+ )}
245
+ >
246
+ {localization.CHANGED_AT}:
247
+ </Text>
248
+ <Text
249
+ className={cn(
250
+ "m-0 text-sm font-semibold",
251
+ classNames?.content
252
+ )}
253
+ >
254
+ {timestamp}
255
+ </Text>
256
+ </Section>
257
+ )}
258
+
259
+ <Text className={cn("text-sm font-normal", classNames?.content)}>
260
+ {localization.IF_YOU_MADE_THIS_CHANGE}
261
+ </Text>
262
+
263
+ {secureAccountURL && (
264
+ <Section className="mt-6">
265
+ <Button
266
+ href={secureAccountURL}
267
+ className={cn(
268
+ "inline-block whitespace-nowrap rounded-none text-sm font-medium py-2.5 px-6 bg-primary text-primary-foreground no-underline",
269
+ classNames?.button
270
+ )}
271
+ >
272
+ {localization.I_DIDNT_MAKE_THIS_CHANGE}
273
+ </Button>
274
+ </Section>
275
+ )}
276
+
277
+ <Hr
278
+ className={cn(
279
+ "my-6 w-full border border-solid border-border",
280
+ classNames?.separator
281
+ )}
282
+ />
283
+
284
+ {appName && (
285
+ <Text
286
+ className={cn(
287
+ "mb-3 text-xs text-muted-foreground",
288
+ classNames?.description
289
+ )}
290
+ >
291
+ {localization.EMAIL_SENT_BY.replace("{appName}", appName)}
292
+ </Text>
293
+ )}
294
+
295
+ <Text
296
+ className={cn(
297
+ "mt-3 text-xs text-muted-foreground",
298
+ classNames?.description
299
+ )}
300
+ >
301
+ {(() => {
302
+ const [beforeSupportEmail, afterSupportEmail] =
303
+ localization.IF_YOU_DIDNT_AUTHORIZE_THIS_CHANGE.split(
304
+ "{supportEmail}"
305
+ )
306
+
307
+ return supportEmail ? (
308
+ <>
309
+ {beforeSupportEmail}
310
+ <Link
311
+ href={`mailto:${supportEmail}`}
312
+ className={cn(
313
+ "text-primary underline",
314
+ classNames?.link
315
+ )}
316
+ >
317
+ {supportEmail}
318
+ </Link>
319
+ {afterSupportEmail}
320
+ </>
321
+ ) : (
322
+ localization.IF_YOU_DIDNT_AUTHORIZE_THIS_CHANGE.replace(
323
+ "{supportEmail}",
324
+ ""
325
+ )
326
+ .replace(/\s{2,}/g, " ")
327
+ .replace(" .", ".")
328
+ )
329
+ })()}
330
+ </Text>
331
+
332
+ {poweredBy && (
333
+ <Text
334
+ className={cn(
335
+ "mt-4 mb-0 text-center text-[11px] text-muted-foreground",
336
+ classNames?.poweredBy
337
+ )}
338
+ >
339
+ {(() => {
340
+ const [beforeBetterAuth, afterBetterAuth] =
341
+ localization.POWERED_BY_BETTER_AUTH.split("{betterAuth}")
342
+
343
+ return (
344
+ <>
345
+ {beforeBetterAuth}
346
+ <Link
347
+ href="https://better-auth.com"
348
+ className={cn(
349
+ "text-primary underline",
350
+ classNames?.link
351
+ )}
352
+ >
353
+ better-auth
354
+ </Link>
355
+ {afterBetterAuth}
356
+ </>
357
+ )
358
+ })()}
359
+ </Text>
360
+ )}
361
+ </Section>
362
+ </Container>
363
+ </Body>
364
+ </Tailwind>
365
+ </Html>
366
+ )
367
+ }
368
+
369
+ PasswordChangedEmail.localization = passwordChangedEmailLocalization
370
+
371
+ PasswordChangedEmail.PreviewProps = {
372
+ email: "m@example.com",
373
+ timestamp: "February 10, 2025 at 4:20 PM UTC",
374
+ secureAccountURL: "https://better-auth-ui.com/settings/security",
375
+ appName: "Better Auth",
376
+ supportEmail: "support@example.com",
377
+ darkMode: true
378
+ } as PasswordChangedEmailProps
379
+
380
+ export default PasswordChangedEmail