@better-auth-ui/heroui 1.6.4 → 1.6.6

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.
@@ -2,135 +2,141 @@ import { FieldSeparator as e } from "../field-separator.js";
2
2
  import { ProviderButtons as t } from "../provider-buttons.js";
3
3
  import { usernamePlugin as n } from "../../../lib/auth/username-plugin.js";
4
4
  import { authMutationKeys as r } from "@better-auth-ui/core";
5
- import { useAuth as i, useAuthPlugin as a, useSendVerificationEmail as o, useSignInEmail as s, useSignInUsername as c } from "@better-auth-ui/react";
6
- import { Button as l, Card as u, Checkbox as d, Description as f, FieldError as p, Form as m, Input as h, Label as g, Link as _, Spinner as v, TextField as y, cn as b, toast as x } from "@heroui/react";
7
- import { useState as S } from "react";
8
- import { Fragment as C, jsx as w, jsxs as T } from "react/jsx-runtime";
9
- import { useIsMutating as E } from "@tanstack/react-query";
5
+ import { useAuth as i, useAuthPlugin as a, useFetchOptions as o, useSendVerificationEmail as s, useSignInEmail as c, useSignInUsername as l } from "@better-auth-ui/react";
6
+ import { Button as ee, Card as u, Checkbox as d, Description as f, FieldError as p, Form as te, Input as m, Label as h, Link as g, Spinner as ne, TextField as _, cn as v, toast as y } from "@heroui/react";
7
+ import { useState as re } from "react";
8
+ import { Fragment as b, jsx as x, jsxs as S } from "react/jsx-runtime";
9
+ import { useIsMutating as C } from "@tanstack/react-query";
10
10
  //#region src/components/auth/username/sign-in-username.tsx
11
- function D({ className: D, socialLayout: O, socialPosition: k = "bottom", variant: A, ...j }) {
12
- let { authClient: M, basePaths: N, baseURL: P, emailAndPassword: F, localization: I, plugins: L, redirectTo: R, socialProviders: z, viewPaths: B, navigate: V } = i(), { localization: H } = a(n), [U, W] = S(""), { mutate: G } = o(M, { onSuccess: () => x.success(I.auth.verificationEmailSent) });
13
- function K(e) {
11
+ function w({ className: w, socialLayout: T, socialPosition: E = "bottom", variant: D, ...O }) {
12
+ let { authClient: k, basePaths: A, baseURL: j, emailAndPassword: M, localization: N, plugins: P, redirectTo: F, socialProviders: I, viewPaths: L, navigate: R } = i(), { fetchOptions: z, resetFetchOptions: B } = o(), { localization: V } = a(n), [H, U] = re(""), { mutate: W } = s(k, { onSuccess: () => y.success(N.auth.verificationEmailSent) });
13
+ function G(e) {
14
14
  return /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(e);
15
15
  }
16
- let { mutate: q } = s(M, {
16
+ let { mutate: K, isPending: q } = c(k, {
17
17
  onError: (e, { email: t }) => {
18
- W(""), e.error?.code === "EMAIL_NOT_VERIFIED" ? x.danger(e.error?.message || e.message, { actionProps: {
19
- children: I.auth.resend,
20
- onClick: () => G({
18
+ U(""), e.error?.code === "EMAIL_NOT_VERIFIED" ? y.danger(e.error?.message || e.message, { actionProps: {
19
+ children: N.auth.resend,
20
+ onClick: () => W({
21
21
  email: t,
22
- callbackURL: `${P}${R}`
22
+ callbackURL: `${j}${F}`
23
23
  })
24
- } }) : x.danger(e.error?.message || e.message);
24
+ } }) : y.danger(e.error?.message || e.message), B();
25
25
  },
26
- onSuccess: () => V({ to: R })
27
- }), { mutate: J, isPending: Y } = c(M, {
26
+ onSuccess: () => R({ to: F })
27
+ }), { mutate: J, isPending: Y } = l(k, {
28
28
  onError: (e) => {
29
- W(""), x.danger(e.error?.message || e.message);
29
+ U(""), y.danger(e.error?.message || e.message), B();
30
30
  },
31
- onSuccess: () => V({ to: R })
32
- }), X = (e) => {
31
+ onSuccess: () => R({ to: F })
32
+ }), ie = (e) => {
33
33
  e.preventDefault();
34
34
  let t = new FormData(e.currentTarget), n = t.get("email"), r = t.get("rememberMe") === "on";
35
- K(n) ? q({
35
+ G(n) ? K({
36
36
  email: n,
37
- password: U,
38
- ...F?.rememberMe ? { rememberMe: r } : {}
37
+ password: H,
38
+ ...M?.rememberMe ? { rememberMe: r } : {},
39
+ fetchOptions: z
39
40
  }) : J({
40
41
  username: n,
41
- password: U,
42
- ...F?.rememberMe ? { rememberMe: r } : {}
42
+ password: H,
43
+ ...M?.rememberMe ? { rememberMe: r } : {},
44
+ fetchOptions: z
43
45
  });
44
- }, Z = E({ mutationKey: r.signIn.all }) + E({ mutationKey: r.signUp.all }) > 0, Q = F?.enabled && !!z?.length;
45
- return /* @__PURE__ */ T(u, {
46
- className: b("w-full max-w-sm gap-4 md:p-6", D),
47
- variant: A,
48
- ...j,
46
+ }, X = C({ mutationKey: r.signIn.all }) + C({ mutationKey: r.signUp.all }) > 0, Z = q || Y, Q = P.find((e) => e.captchaComponent)?.captchaComponent, $ = M?.enabled && !!I?.length;
47
+ return /* @__PURE__ */ S(u, {
48
+ className: v("w-full max-w-sm gap-4 md:p-6", w),
49
+ variant: D,
50
+ ...O,
49
51
  children: [
50
- /* @__PURE__ */ w(u.Header, { children: /* @__PURE__ */ w(u.Title, {
52
+ /* @__PURE__ */ x(u.Header, { children: /* @__PURE__ */ x(u.Title, {
51
53
  className: "text-xl font-semibold mb-1",
52
- children: I.auth.signIn
54
+ children: N.auth.signIn
53
55
  }) }),
54
- /* @__PURE__ */ T(u.Content, {
56
+ /* @__PURE__ */ S(u.Content, {
55
57
  className: "gap-4",
56
58
  children: [
57
- k === "top" && /* @__PURE__ */ T(C, { children: [!!z?.length && /* @__PURE__ */ w(t, { socialLayout: O }), Q && /* @__PURE__ */ w(e, { children: I.auth.or })] }),
58
- F?.enabled && /* @__PURE__ */ T(m, {
59
- onSubmit: X,
59
+ E === "top" && /* @__PURE__ */ S(b, { children: [!!I?.length && /* @__PURE__ */ x(t, { socialLayout: T }), $ && /* @__PURE__ */ x(e, { children: N.auth.or })] }),
60
+ M?.enabled && /* @__PURE__ */ S(te, {
61
+ onSubmit: ie,
60
62
  className: "flex flex-col gap-4",
61
63
  children: [
62
- /* @__PURE__ */ T(y, {
64
+ /* @__PURE__ */ S(_, {
63
65
  name: "email",
64
66
  type: "text",
65
67
  autoComplete: "username email",
66
- isDisabled: Z,
68
+ isDisabled: X,
67
69
  children: [
68
- /* @__PURE__ */ w(g, { children: H.username }),
69
- /* @__PURE__ */ w(h, {
70
- placeholder: H.usernameOrEmailPlaceholder,
71
- variant: A === "transparent" ? "primary" : "secondary",
70
+ /* @__PURE__ */ x(h, { children: V.username }),
71
+ /* @__PURE__ */ x(m, {
72
+ placeholder: V.usernameOrEmailPlaceholder,
73
+ variant: D === "transparent" ? "primary" : "secondary",
72
74
  required: !0
73
75
  }),
74
- /* @__PURE__ */ w(p, {})
76
+ /* @__PURE__ */ x(p, {})
75
77
  ]
76
78
  }),
77
- /* @__PURE__ */ T(y, {
78
- minLength: F?.minPasswordLength,
79
- maxLength: F?.maxPasswordLength,
79
+ /* @__PURE__ */ S(_, {
80
+ minLength: M?.minPasswordLength,
81
+ maxLength: M?.maxPasswordLength,
80
82
  name: "password",
81
83
  type: "password",
82
84
  autoComplete: "current-password",
83
- isDisabled: Z,
84
- value: U,
85
- onChange: W,
85
+ isDisabled: X,
86
+ value: H,
87
+ onChange: U,
86
88
  children: [
87
- /* @__PURE__ */ w(g, { children: I.auth.password }),
88
- /* @__PURE__ */ w(h, {
89
- placeholder: I.auth.passwordPlaceholder,
90
- variant: A === "transparent" ? "primary" : "secondary",
89
+ /* @__PURE__ */ x(h, { children: N.auth.password }),
90
+ /* @__PURE__ */ x(m, {
91
+ placeholder: N.auth.passwordPlaceholder,
92
+ variant: D === "transparent" ? "primary" : "secondary",
91
93
  required: !0
92
94
  }),
93
- /* @__PURE__ */ w(p, {})
95
+ /* @__PURE__ */ x(p, {})
94
96
  ]
95
97
  }),
96
- F?.rememberMe && /* @__PURE__ */ T(d, {
98
+ M?.rememberMe && /* @__PURE__ */ S(d, {
97
99
  name: "rememberMe",
98
- isDisabled: Z,
99
- variant: A === "transparent" ? "primary" : "secondary",
100
- children: [/* @__PURE__ */ w(d.Control, { children: /* @__PURE__ */ w(d.Indicator, {}) }), /* @__PURE__ */ w(d.Content, { children: /* @__PURE__ */ w(g, { children: I.auth.rememberMe }) })]
100
+ isDisabled: X,
101
+ variant: D === "transparent" ? "primary" : "secondary",
102
+ children: [/* @__PURE__ */ x(d.Control, { children: /* @__PURE__ */ x(d.Indicator, {}) }), /* @__PURE__ */ x(d.Content, { children: /* @__PURE__ */ x(h, { children: N.auth.rememberMe }) })]
101
103
  }),
102
- /* @__PURE__ */ T("div", {
104
+ Q && /* @__PURE__ */ x("div", {
105
+ className: "flex justify-center",
106
+ children: Q
107
+ }),
108
+ /* @__PURE__ */ S("div", {
103
109
  className: "flex flex-col gap-3",
104
- children: [/* @__PURE__ */ T(l, {
110
+ children: [/* @__PURE__ */ S(ee, {
105
111
  type: "submit",
106
112
  className: "w-full",
107
- isPending: Y || Z,
108
- children: [Y && /* @__PURE__ */ w(v, {
113
+ isPending: Z || X,
114
+ children: [Z && /* @__PURE__ */ x(ne, {
109
115
  color: "current",
110
116
  size: "sm"
111
- }), I.auth.signIn]
112
- }), L.flatMap((e) => e.authButtons?.map((t, n) => /* @__PURE__ */ w(t, { view: "signIn" }, `${e.id}-${n.toString()}`)))]
117
+ }), N.auth.signIn]
118
+ }), P.flatMap((e) => e.authButtons?.map((t, n) => /* @__PURE__ */ x(t, { view: "signIn" }, `${e.id}-${n.toString()}`)))]
113
119
  })
114
120
  ]
115
121
  }),
116
- k === "bottom" && /* @__PURE__ */ T(C, { children: [Q && /* @__PURE__ */ w(e, { children: I.auth.or }), !!z?.length && /* @__PURE__ */ w(t, { socialLayout: O })] })
122
+ E === "bottom" && /* @__PURE__ */ S(b, { children: [$ && /* @__PURE__ */ x(e, { children: N.auth.or }), !!I?.length && /* @__PURE__ */ x(t, { socialLayout: T })] })
117
123
  ]
118
124
  }),
119
- /* @__PURE__ */ T(u.Footer, {
125
+ /* @__PURE__ */ S(u.Footer, {
120
126
  className: "flex-col gap-3",
121
- children: [F?.forgotPassword && /* @__PURE__ */ w(_, {
122
- href: `${N.auth}/${B.auth.forgotPassword}`,
127
+ children: [M?.forgotPassword && /* @__PURE__ */ x(g, {
128
+ href: `${A.auth}/${L.auth.forgotPassword}`,
123
129
  className: "no-underline hover:underline",
124
- children: I.auth.forgotPasswordLink
125
- }), F?.enabled && /* @__PURE__ */ T(f, {
130
+ children: N.auth.forgotPasswordLink
131
+ }), M?.enabled && /* @__PURE__ */ S(f, {
126
132
  className: "text-sm",
127
133
  children: [
128
- I.auth.needToCreateAnAccount,
134
+ N.auth.needToCreateAnAccount,
129
135
  " ",
130
- /* @__PURE__ */ w(_, {
131
- href: `${N.auth}/${B.auth.signUp}`,
136
+ /* @__PURE__ */ x(g, {
137
+ href: `${A.auth}/${L.auth.signUp}`,
132
138
  className: "text-accent no-underline hover:underline decoration-accent-hover",
133
- children: I.auth.signUp
139
+ children: N.auth.signUp
134
140
  })
135
141
  ]
136
142
  })]
@@ -139,4 +145,4 @@ function D({ className: D, socialLayout: O, socialPosition: k = "bottom", varian
139
145
  });
140
146
  }
141
147
  //#endregion
142
- export { D as SignInUsername };
148
+ export { w as SignInUsername };
@@ -26,7 +26,6 @@ function h({ name: h, field: g, isPending: _, variant: v }) {
26
26
  minLength: x,
27
27
  maxLength: S,
28
28
  isDisabled: _,
29
- isRequired: g.required,
30
29
  isReadOnly: g.readOnly,
31
30
  value: T,
32
31
  onChange: M,
@@ -34,7 +33,10 @@ function h({ name: h, field: g, isPending: _, variant: v }) {
34
33
  /* @__PURE__ */ f(c, { children: g.label }),
35
34
  /* @__PURE__ */ p(s, {
36
35
  variant: v === "transparent" ? "primary" : "secondary",
37
- children: [/* @__PURE__ */ f(s.Input, { placeholder: g.placeholder }), N && /* @__PURE__ */ f(s.Suffix, {
36
+ children: [/* @__PURE__ */ f(s.Input, {
37
+ placeholder: g.placeholder,
38
+ required: g.required
39
+ }), N && /* @__PURE__ */ f(s.Suffix, {
38
40
  "aria-label": O?.available ? b.usernameAvailable : O?.available === !1 ? b.usernameTaken : void 0,
39
41
  className: "px-2",
40
42
  children: O?.available ? /* @__PURE__ */ f(i, { className: "text-success" }) : k || O?.available === !1 ? /* @__PURE__ */ f(a, { className: "text-danger" }) : /* @__PURE__ */ f(l, {
package/package.json CHANGED
@@ -1,10 +1,10 @@
1
1
  {
2
2
  "name": "@better-auth-ui/heroui",
3
- "version": "1.6.4",
3
+ "version": "1.6.6",
4
4
  "type": "module",
5
5
  "scripts": {
6
6
  "build": "vite build",
7
- "dev": "vite build --watch",
7
+ "dev": "tsc --watch",
8
8
  "test": "vitest"
9
9
  },
10
10
  "files": [
@@ -28,11 +28,11 @@
28
28
  },
29
29
  "devDependencies": {
30
30
  "@gravity-ui/icons": "^2.18.0",
31
- "@heroui/react": "^3.0.2",
32
- "@heroui/styles": "^3.0.2",
31
+ "@heroui/react": "^3.0.4",
32
+ "@heroui/styles": "^3.0.4",
33
33
  "@internationalized/date": "^3.12.1",
34
34
  "@tanstack/react-pacer": "^0.22.0",
35
- "@tanstack/react-query": "^5.100.8",
35
+ "@tanstack/react-query": "^5.100.9",
36
36
  "@testing-library/react": "^16.3.2",
37
37
  "@testing-library/user-event": "^14.5.2",
38
38
  "@types/react": "^19.2.14",
@@ -41,22 +41,22 @@
41
41
  "better-auth": "^1.6.9",
42
42
  "bowser": "^2.11.0",
43
43
  "playwright": "^1.59.1",
44
- "react": "^19.2.5",
45
- "react-dom": "^19.2.5",
44
+ "react": "^19.2.6",
45
+ "react-dom": "^19.2.6",
46
46
  "vitest": "^4.1.5"
47
47
  },
48
48
  "peerDependencies": {
49
49
  "@better-auth-ui/core": "*",
50
50
  "@better-auth-ui/react": "*",
51
51
  "@gravity-ui/icons": ">=2.18.0",
52
- "@heroui/react": ">=3.0.2",
53
- "@heroui/styles": ">=3.0.2",
52
+ "@heroui/react": ">=3.0.4",
53
+ "@heroui/styles": ">=3.0.4",
54
54
  "@internationalized/date": ">=3.12.1",
55
55
  "@tanstack/react-pacer": ">=0.22.0",
56
- "@tanstack/react-query": ">=5.100.8",
56
+ "@tanstack/react-query": ">=5.100.9",
57
57
  "better-auth": ">=1.6.9",
58
- "react": ">=19.2.5",
59
- "react-dom": ">=19.2.5",
58
+ "react": ">=19.2.6",
59
+ "react-dom": ">=19.2.6",
60
60
  "bowser": ">=2.11.0"
61
61
  },
62
62
  "repository": {
@@ -1,8 +1,11 @@
1
1
  import { useAuth } from "@better-auth-ui/react"
2
2
  import { type CardProps, cn } from "@heroui/react"
3
3
  import type { ComponentProps } from "react"
4
+
4
5
  import { DeleteUser } from "./delete-user"
5
- export type DangerZoneProps = Omit<ComponentProps<"div">, "children"> & {
6
+
7
+ export type DangerZoneProps = {
8
+ className?: string
6
9
  variant?: CardProps["variant"]
7
10
  }
8
11
 
@@ -10,12 +13,16 @@ export type DangerZoneProps = Omit<ComponentProps<"div">, "children"> & {
10
13
  * Renders the danger zone heading and {@link DeleteUser}.
11
14
  * Registered as a `securityCard` by `deleteUserPlugin()`; gate by registering the plugin.
12
15
  */
13
- export function DangerZone({ className, variant, ...props }: DangerZoneProps) {
16
+ export function DangerZone({
17
+ className,
18
+ variant,
19
+ ...props
20
+ }: DangerZoneProps & ComponentProps<"div">) {
14
21
  const { localization } = useAuth()
15
22
 
16
23
  return (
17
24
  <div className={cn("flex w-full flex-col", className)} {...props}>
18
- <h2 className={cn("text-sm font-semibold mb-3")}>
25
+ <h2 className={cn("text-sm font-semibold mb-3 text-danger")}>
19
26
  {localization.settings.dangerZone}
20
27
  </h2>
21
28
 
@@ -11,7 +11,6 @@ import {
11
11
  Button,
12
12
  Card,
13
13
  type CardProps,
14
- cn,
15
14
  FieldError,
16
15
  Form,
17
16
  Input,
@@ -91,11 +90,7 @@ export function DeleteUser({
91
90
  }
92
91
 
93
92
  return (
94
- <Card
95
- className={cn("border border-danger", className)}
96
- variant={variant}
97
- {...props}
98
- >
93
+ <Card className={className} variant={variant} {...props}>
99
94
  <Card.Content className="flex flex-col gap-4 sm:flex-row sm:items-center sm:justify-between">
100
95
  <div>
101
96
  <p className="text-sm font-medium leading-tight">
@@ -111,7 +106,7 @@ export function DeleteUser({
111
106
  <Button
112
107
  isDisabled={!accounts}
113
108
  size="sm"
114
- variant="danger"
109
+ variant="danger-soft"
115
110
  onPress={() => setConfirmOpen(true)}
116
111
  >
117
112
  {deleteUserLocalization.deleteUser}
@@ -1,4 +1,8 @@
1
- import { useAuth, useRequestPasswordReset } from "@better-auth-ui/react"
1
+ import {
2
+ useAuth,
3
+ useFetchOptions,
4
+ useRequestPasswordReset
5
+ } from "@better-auth-ui/react"
2
6
  import {
3
7
  Button,
4
8
  Card,
@@ -35,11 +39,18 @@ export function ForgotPassword({
35
39
  variant,
36
40
  ...props
37
41
  }: ForgotPasswordProps & Omit<CardProps, "children">) {
38
- const { authClient, basePaths, localization, viewPaths, navigate } = useAuth()
42
+ const { authClient, basePaths, localization, viewPaths, navigate, plugins } =
43
+ useAuth()
44
+
45
+ const { fetchOptions, resetFetchOptions } = useFetchOptions()
39
46
 
40
47
  const { mutate: requestPasswordReset, isPending } = useRequestPasswordReset(
41
48
  authClient,
42
49
  {
50
+ onError: (error) => {
51
+ toast.danger(error.error?.message || error.message)
52
+ resetFetchOptions()
53
+ },
43
54
  onSuccess: () => {
44
55
  toast.success(localization.auth.passwordResetEmailSent)
45
56
  navigate({ to: `${basePaths.auth}/${viewPaths.auth.signIn}` })
@@ -51,9 +62,16 @@ export function ForgotPassword({
51
62
  e.preventDefault()
52
63
 
53
64
  const formData = new FormData(e.currentTarget)
54
- requestPasswordReset({ email: formData.get("email") as string })
65
+ requestPasswordReset({
66
+ email: formData.get("email") as string,
67
+ fetchOptions
68
+ })
55
69
  }
56
70
 
71
+ const Captcha = plugins.find(
72
+ (plugin) => plugin.captchaComponent
73
+ )?.captchaComponent
74
+
57
75
  return (
58
76
  <Card
59
77
  className={cn("w-full max-w-sm gap-4 md:p-6", className)}
@@ -85,11 +103,15 @@ export function ForgotPassword({
85
103
  <FieldError />
86
104
  </TextField>
87
105
 
88
- <Button type="submit" className="w-full" isPending={isPending}>
89
- {isPending && <Spinner color="current" size="sm" />}
106
+ {Captcha && <div className="flex justify-center">{Captcha}</div>}
107
+
108
+ <div className="flex flex-col gap-3">
109
+ <Button type="submit" className="w-full" isPending={isPending}>
110
+ {isPending && <Spinner color="current" size="sm" />}
90
111
 
91
- {localization.auth.sendResetLink}
92
- </Button>
112
+ {localization.auth.sendResetLink}
113
+ </Button>
114
+ </div>
93
115
  </Form>
94
116
  </Card.Content>
95
117
 
@@ -1,6 +1,7 @@
1
1
  import { authMutationKeys } from "@better-auth-ui/core"
2
2
  import {
3
3
  useAuth,
4
+ useFetchOptions,
4
5
  useSendVerificationEmail,
5
6
  useSignInEmail
6
7
  } from "@better-auth-ui/react"
@@ -58,6 +59,8 @@ export function SignIn({
58
59
  navigate
59
60
  } = useAuth()
60
61
 
62
+ const { fetchOptions, resetFetchOptions } = useFetchOptions()
63
+
61
64
  const [password, setPassword] = useState("")
62
65
 
63
66
  const { mutate: sendVerificationEmail } = useSendVerificationEmail(
@@ -87,6 +90,8 @@ export function SignIn({
87
90
  } else {
88
91
  toast.danger(error.error?.message || error.message)
89
92
  }
93
+
94
+ resetFetchOptions()
90
95
  },
91
96
  onSuccess: () => navigate({ to: redirectTo })
92
97
  }
@@ -102,7 +107,8 @@ export function SignIn({
102
107
  signInEmail({
103
108
  email,
104
109
  password,
105
- ...(emailAndPassword?.rememberMe ? { rememberMe } : {})
110
+ ...(emailAndPassword?.rememberMe ? { rememberMe } : {}),
111
+ fetchOptions
106
112
  })
107
113
  }
108
114
 
@@ -114,6 +120,10 @@ export function SignIn({
114
120
  })
115
121
  const isPending = signInMutating + signUpMutating > 0
116
122
 
123
+ const Captcha = plugins.find(
124
+ (plugin) => plugin.captchaComponent
125
+ )?.captchaComponent
126
+
117
127
  const showSeparator = emailAndPassword?.enabled && !!socialProviders?.length
118
128
 
119
129
  return (
@@ -197,6 +207,8 @@ export function SignIn({
197
207
  </Checkbox>
198
208
  )}
199
209
 
210
+ {Captcha && <div className="flex justify-center">{Captcha}</div>}
211
+
200
212
  <div className="flex flex-col gap-3">
201
213
  <Button type="submit" className="w-full" isPending={isPending}>
202
214
  {signInEmailPending && <Spinner color="current" size="sm" />}
@@ -2,7 +2,7 @@ import {
2
2
  authMutationKeys,
3
3
  parseAdditionalFieldValue
4
4
  } from "@better-auth-ui/core"
5
- import { useAuth, useSignUpEmail } from "@better-auth-ui/react"
5
+ import { useAuth, useFetchOptions, useSignUpEmail } from "@better-auth-ui/react"
6
6
  import { Eye, EyeSlash } from "@gravity-ui/icons"
7
7
  import {
8
8
  Button,
@@ -63,6 +63,8 @@ export function SignUp({
63
63
  navigate
64
64
  } = useAuth()
65
65
 
66
+ const { fetchOptions, resetFetchOptions } = useFetchOptions()
67
+
66
68
  const [password, setPassword] = useState("")
67
69
  const [confirmPassword, setConfirmPassword] = useState("")
68
70
 
@@ -73,6 +75,7 @@ export function SignUp({
73
75
  setPassword("")
74
76
  setConfirmPassword("")
75
77
  toast.danger(error.error?.message || error.message)
78
+ resetFetchOptions()
76
79
  },
77
80
  onSuccess: () => {
78
81
  if (emailAndPassword?.requireEmailVerification) {
@@ -97,6 +100,10 @@ export function SignUp({
97
100
  })
98
101
  const isPending = signInMutating + signUpMutating > 0
99
102
 
103
+ const Captcha = plugins.find(
104
+ (plugin) => plugin.captchaComponent
105
+ )?.captchaComponent
106
+
100
107
  const handleSubmit = async (e: SyntheticEvent<HTMLFormElement>) => {
101
108
  e.preventDefault()
102
109
 
@@ -139,7 +146,8 @@ export function SignUp({
139
146
  name,
140
147
  email,
141
148
  password,
142
- ...additionalFieldValues
149
+ ...additionalFieldValues,
150
+ fetchOptions
143
151
  })
144
152
  }
145
153
 
@@ -323,6 +331,8 @@ export function SignUp({
323
331
  )
324
332
  )}
325
333
 
334
+ {Captcha && <div className="flex justify-center">{Captcha}</div>}
335
+
326
336
  <div className="flex flex-col gap-3">
327
337
  <Button type="submit" className="w-full" isPending={isPending}>
328
338
  {signUpEmailPending && <Spinner color="current" size="sm" />}
@@ -1,5 +1,5 @@
1
1
  import { useAuthPlugin } from "@better-auth-ui/react"
2
- import { Display, Moon, Sun } from "@gravity-ui/icons"
2
+ import { Display, Moon, Palette, Sun } from "@gravity-ui/icons"
3
3
  import { Dropdown, Label, Tabs } from "@heroui/react"
4
4
 
5
5
  import { themePlugin } from "../../../lib/auth/theme-plugin"
@@ -67,6 +67,7 @@ export function ThemeToggleItem() {
67
67
  // menu-item activation.
68
68
  shouldCloseOnSelect={false}
69
69
  >
70
+ <Palette className="text-muted" />
70
71
  <Label>{localization.theme}</Label>
71
72
 
72
73
  <Tabs