@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,452 @@
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
+ /**
27
+ * Device information displayed in the new device email notification.
28
+ */
29
+ export interface DeviceInfo {
30
+ /** Browser name and version */
31
+ browser?: string
32
+ /** Operating system name and version */
33
+ os?: string
34
+ /** Geographic location of the sign-in */
35
+ location?: string
36
+ /** IP address of the device */
37
+ ipAddress?: string
38
+ /** Timestamp of the sign-in event */
39
+ timestamp?: string
40
+ }
41
+
42
+ const newDeviceEmailLocalization = {
43
+ NEW_SIGN_IN_DETECTED: "New sign-in detected",
44
+ LOGO: "Logo",
45
+ NEW_SIGN_IN_TO_YOUR_ACCOUNT:
46
+ "We detected a new sign-in to your {appName} account {userEmail} from a device we don't recognize.",
47
+ DEVICE_DETAILS: "Device details",
48
+ BROWSER: "Browser",
49
+ OPERATING_SYSTEM: "Operating System",
50
+ LOCATION: "Location",
51
+ IP_ADDRESS: "IP Address",
52
+ TIME: "Time",
53
+ IF_THIS_WAS_YOU:
54
+ "If this was you, you can safely ignore this email. If you don't recognize this activity, please secure your account immediately.",
55
+ SECURE_MY_ACCOUNT: "Secure my account",
56
+ EMAIL_SENT_BY: "Email sent by {appName}.",
57
+ IF_YOU_DIDNT_SIGN_IN:
58
+ "If you didn't sign in, please contact support immediately {supportEmail} to secure your account.",
59
+ POWERED_BY_BETTER_AUTH: "Powered by {betterAuth}"
60
+ }
61
+
62
+ /**
63
+ * Localization strings for the NewDeviceEmail component.
64
+ *
65
+ * Contains all text content used in the new device detection email template.
66
+ */
67
+ export type NewDeviceEmailLocalization = typeof newDeviceEmailLocalization
68
+
69
+ /**
70
+ * Props for the NewDeviceEmail component.
71
+ */
72
+ export interface NewDeviceEmailProps {
73
+ /** Email address of the user account */
74
+ userEmail?: string
75
+ /** Information about the device that signed in */
76
+ deviceInfo?: DeviceInfo
77
+ /** URL to secure the account if unauthorized access is suspected */
78
+ secureAccountLink?: string
79
+ /** Name of the application sending the email */
80
+ appName?: string
81
+ /** Support email address for security concerns */
82
+ supportEmail?: string
83
+ /** Logo URL(s) - a single string or light/dark variants. If omitted, no logo is shown. */
84
+ logoURL?: string | { light: string; dark: string }
85
+ /** Custom CSS class names for styling specific parts of the email */
86
+ classNames?: EmailClassNames
87
+ /** Custom color scheme for light and dark modes */
88
+ colors?: EmailColors
89
+ /** Whether to show the "Powered by better-auth" footer */
90
+ poweredBy?: boolean
91
+ /** Whether to enable dark mode support */
92
+ darkMode?: boolean
93
+ /** Additional React nodes to inject into the email head */
94
+ head?: ReactNode
95
+ /**
96
+ * Localization overrides for customizing email text
97
+ * @remarks `NewDeviceEmailLocalization`
98
+ */
99
+ localization?: Partial<NewDeviceEmailLocalization>
100
+ }
101
+
102
+ /**
103
+ * Email template component that notifies users when a new device signs into their account.
104
+ *
105
+ * This email includes:
106
+ * - Device information display (browser, OS, location, IP, timestamp)
107
+ * - Secure account action button
108
+ * - Security warnings and support contact information
109
+ * - Customizable branding and styling
110
+ * - Support for light/dark mode themes
111
+ *
112
+ * @example
113
+ * ```tsx
114
+ * <NewDeviceEmail
115
+ * userEmail="user@example.com"
116
+ * deviceInfo={{
117
+ * browser: "Chrome on macOS",
118
+ * os: "macOS 14.0",
119
+ * location: "San Francisco, CA",
120
+ * ipAddress: "192.168.1.1",
121
+ * timestamp: "February 10, 2025 at 4:20 PM UTC"
122
+ * }}
123
+ * secureAccountLink="https://example.com/secure-account"
124
+ * appName="My App"
125
+ * supportEmail="support@example.com"
126
+ * darkMode={true}
127
+ * />
128
+ * ```
129
+ */
130
+ export const NewDeviceEmail = ({
131
+ userEmail,
132
+ deviceInfo,
133
+ secureAccountLink,
134
+ appName,
135
+ supportEmail,
136
+ logoURL,
137
+ colors,
138
+ classNames,
139
+ darkMode = true,
140
+ poweredBy,
141
+ head,
142
+ ...props
143
+ }: NewDeviceEmailProps) => {
144
+ const localization = {
145
+ ...NewDeviceEmail.localization,
146
+ ...props.localization
147
+ }
148
+
149
+ const previewText = localization.NEW_SIGN_IN_DETECTED
150
+
151
+ return (
152
+ <Html>
153
+ <Head>
154
+ <meta content="light dark" name="color-scheme" />
155
+ <meta content="light dark" name="supported-color-schemes" />
156
+
157
+ <EmailStyles colors={colors} darkMode={darkMode} />
158
+
159
+ {head}
160
+ </Head>
161
+
162
+ <Preview>{previewText}</Preview>
163
+
164
+ <Tailwind config={{ presets: [pixelBasedPreset] }}>
165
+ <Body className={cn("bg-background font-sans", classNames?.body)}>
166
+ <Container
167
+ className={cn(
168
+ "mx-auto my-auto max-w-xl px-2 py-10",
169
+ classNames?.container
170
+ )}
171
+ >
172
+ <Section
173
+ className={cn(
174
+ "bg-card text-card-foreground rounded-none border border-border p-8",
175
+ classNames?.card
176
+ )}
177
+ >
178
+ {logoURL &&
179
+ (typeof logoURL === "string" ? (
180
+ <Img
181
+ src={logoURL}
182
+ width={48}
183
+ height={48}
184
+ alt={appName || localization.LOGO}
185
+ className={cn("mx-auto mb-8", classNames?.logo)}
186
+ />
187
+ ) : (
188
+ <>
189
+ <Img
190
+ src={logoURL.light}
191
+ width={48}
192
+ height={48}
193
+ alt={appName || localization.LOGO}
194
+ className={cn(
195
+ "mx-auto mb-8 logo-light",
196
+ classNames?.logo
197
+ )}
198
+ />
199
+ <Img
200
+ src={logoURL.dark}
201
+ width={48}
202
+ height={48}
203
+ alt={appName || localization.LOGO}
204
+ className={cn(
205
+ "hidden mx-auto mb-8 logo-dark",
206
+ classNames?.logo
207
+ )}
208
+ />
209
+ </>
210
+ ))}
211
+
212
+ <Heading
213
+ className={cn(
214
+ "m-0 mb-5 text-2xl font-semibold",
215
+ classNames?.title
216
+ )}
217
+ >
218
+ {localization.NEW_SIGN_IN_DETECTED}
219
+ </Heading>
220
+
221
+ <Text className={cn("text-sm font-normal", classNames?.content)}>
222
+ {(() => {
223
+ const textWithAppName =
224
+ localization.NEW_SIGN_IN_TO_YOUR_ACCOUNT.replace(
225
+ "{appName}",
226
+ appName || ""
227
+ )
228
+ .replace(/\s{2,}/g, " ")
229
+ .replace(" .", ".")
230
+
231
+ const [beforeUserEmail, afterUserEmail] =
232
+ textWithAppName.split("{userEmail}")
233
+
234
+ return userEmail ? (
235
+ <>
236
+ {beforeUserEmail}
237
+
238
+ <Link
239
+ href={`mailto:${userEmail}`}
240
+ className="text-primary font-medium"
241
+ >
242
+ {userEmail}
243
+ </Link>
244
+
245
+ {afterUserEmail}
246
+ </>
247
+ ) : (
248
+ textWithAppName
249
+ .replace("{userEmail}", "")
250
+ .replace(/\s{2,}/g, " ")
251
+ .replace(" .", ".")
252
+ )
253
+ })()}
254
+ </Text>
255
+
256
+ {deviceInfo && (
257
+ <Section
258
+ className={cn(
259
+ "my-6 border border-border bg-muted p-4",
260
+ classNames?.codeBlock
261
+ )}
262
+ >
263
+ <Text
264
+ className={cn(
265
+ "m-0 mb-3 text-xs text-muted-foreground",
266
+ classNames?.description
267
+ )}
268
+ >
269
+ {localization.DEVICE_DETAILS}:
270
+ </Text>
271
+
272
+ {deviceInfo.browser && (
273
+ <Text
274
+ className={cn("m-0 mb-2 text-sm", classNames?.content)}
275
+ >
276
+ <span className="font-semibold">
277
+ {localization.BROWSER}:
278
+ </span>{" "}
279
+ {deviceInfo.browser}
280
+ </Text>
281
+ )}
282
+
283
+ {deviceInfo.os && (
284
+ <Text
285
+ className={cn("m-0 mb-2 text-sm", classNames?.content)}
286
+ >
287
+ <span className="font-semibold">
288
+ {localization.OPERATING_SYSTEM}:
289
+ </span>{" "}
290
+ {deviceInfo.os}
291
+ </Text>
292
+ )}
293
+
294
+ {deviceInfo.location && (
295
+ <Text
296
+ className={cn("m-0 mb-2 text-sm", classNames?.content)}
297
+ >
298
+ <span className="font-semibold">
299
+ {localization.LOCATION}:
300
+ </span>{" "}
301
+ {deviceInfo.location}
302
+ </Text>
303
+ )}
304
+
305
+ {deviceInfo.ipAddress && (
306
+ <Text
307
+ className={cn("m-0 mb-2 text-sm", classNames?.content)}
308
+ >
309
+ <span className="font-semibold">
310
+ {localization.IP_ADDRESS}:
311
+ </span>{" "}
312
+ {deviceInfo.ipAddress}
313
+ </Text>
314
+ )}
315
+
316
+ {deviceInfo.timestamp && (
317
+ <Text className={cn("m-0 text-sm", classNames?.content)}>
318
+ <span className="font-semibold">
319
+ {localization.TIME}:
320
+ </span>{" "}
321
+ {deviceInfo.timestamp}
322
+ </Text>
323
+ )}
324
+ </Section>
325
+ )}
326
+
327
+ <Text className={cn("text-sm font-normal", classNames?.content)}>
328
+ {localization.IF_THIS_WAS_YOU}
329
+ </Text>
330
+
331
+ {secureAccountLink && (
332
+ <Section className="mt-6">
333
+ <Button
334
+ href={secureAccountLink}
335
+ className={cn(
336
+ "inline-block whitespace-nowrap rounded-none text-sm font-medium py-2.5 px-6 bg-primary text-primary-foreground no-underline",
337
+ classNames?.button
338
+ )}
339
+ >
340
+ {localization.SECURE_MY_ACCOUNT}
341
+ </Button>
342
+ </Section>
343
+ )}
344
+
345
+ <Hr
346
+ className={cn(
347
+ "my-6 w-full border border-solid border-border",
348
+ classNames?.separator
349
+ )}
350
+ />
351
+
352
+ {appName && (
353
+ <Text
354
+ className={cn(
355
+ "mb-3 text-xs text-muted-foreground",
356
+ classNames?.description
357
+ )}
358
+ >
359
+ {localization.EMAIL_SENT_BY.replace("{appName}", appName)}
360
+ </Text>
361
+ )}
362
+
363
+ <Text
364
+ className={cn(
365
+ "mt-3 text-xs text-muted-foreground",
366
+ classNames?.description
367
+ )}
368
+ >
369
+ {(() => {
370
+ const [beforeSupportEmail, afterSupportEmail] =
371
+ localization.IF_YOU_DIDNT_SIGN_IN.split("{supportEmail}")
372
+
373
+ return supportEmail ? (
374
+ <>
375
+ {beforeSupportEmail}
376
+ <Link
377
+ href={`mailto:${supportEmail}`}
378
+ className={cn(
379
+ "text-primary underline",
380
+ classNames?.link
381
+ )}
382
+ >
383
+ {supportEmail}
384
+ </Link>
385
+ {afterSupportEmail}
386
+ </>
387
+ ) : (
388
+ localization.IF_YOU_DIDNT_SIGN_IN.replace(
389
+ "{supportEmail}",
390
+ ""
391
+ )
392
+ .replace(/\s{2,}/g, " ")
393
+ .replace(" .", ".")
394
+ )
395
+ })()}
396
+ </Text>
397
+
398
+ {poweredBy && (
399
+ <Text
400
+ className={cn(
401
+ "mt-4 mb-0 text-center text-[11px] text-muted-foreground",
402
+ classNames?.poweredBy
403
+ )}
404
+ >
405
+ {(() => {
406
+ const [beforeBetterAuth, afterBetterAuth] =
407
+ localization.POWERED_BY_BETTER_AUTH.split("{betterAuth}")
408
+
409
+ return (
410
+ <>
411
+ {beforeBetterAuth}
412
+ <Link
413
+ href="https://better-auth.com"
414
+ className={cn(
415
+ "text-primary underline",
416
+ classNames?.link
417
+ )}
418
+ >
419
+ better-auth
420
+ </Link>
421
+ {afterBetterAuth}
422
+ </>
423
+ )
424
+ })()}
425
+ </Text>
426
+ )}
427
+ </Section>
428
+ </Container>
429
+ </Body>
430
+ </Tailwind>
431
+ </Html>
432
+ )
433
+ }
434
+
435
+ NewDeviceEmail.localization = newDeviceEmailLocalization
436
+
437
+ NewDeviceEmail.PreviewProps = {
438
+ userEmail: "m@example.com",
439
+ deviceInfo: {
440
+ browser: "Chrome on macOS",
441
+ os: "macOS 26.2",
442
+ location: "San Francisco, CA, United States",
443
+ ipAddress: "127.0.0.1",
444
+ timestamp: "February 10, 2025 at 4:20 PM UTC"
445
+ },
446
+ secureAccountLink: "https://better-auth-ui.com/auth/secure-account",
447
+ appName: "Better Auth",
448
+ supportEmail: "support@example.com",
449
+ darkMode: true
450
+ } as NewDeviceEmailProps
451
+
452
+ export default NewDeviceEmail