BETTER-AUTH-UI.
GitHub ↗

Mutations

TanStack Query primitives for every Better Auth write endpoint.

React provides mutation hooks. Core provides the shared options factories. Each factory exposes its canonical key through .mutationKey.

import { authClient } from "@/lib/auth-client"
import { signInEmailOptions } from "@better-auth-ui/core"
import { useSignInEmail } from "@better-auth-ui/react"

Use the hook in components. Use the core factory where TanStack Query accepts a mutationOptions object.

Error handling

Each mutation adds throw: true to fetchOptions. The promise rejects with BetterFetchError instead of resolving with { error }.

You can therefore use the standard error, isError, throwOnError, and onError values from useMutation.

const { mutate, error } = useSignInEmail(authClient, {
  onError: (err) => toast.error(err.message)
})

Cache side effects

Hooks that change authentication state also update the cache. A custom onSuccess handler runs after the built-in cache update:

  • useSignInEmail / useSignInUsername / useSignInPasskey / useSignUpEmail: reset the session query so it retrieves the new session.
  • useSignInSocial / useSignInMagicLink: redirect without a cache update.
  • useSignOut: remove every ["auth", ...] query.
  • useUpdateUser: update the current user's profile fields and refresh the cached session.
  • useSetActiveSession: update the cached session, scroll to the top, and refresh the session queries.
  • useChangeEmail / useChangeEmailOtp: refresh the session.
  • useSignInEmailOtp / useVerifyEmailOtp: refresh the session because both operations create one.
  • useVerifyTotp / useVerifyTwoFactorOtp / useVerifyBackupCode: refresh the session after the second factor creates it.
  • useEnableTwoFactor / useDisableTwoFactor: refresh the session to update user.twoFactorEnabled.
  • useAddPasskey / useDeletePasskey: refresh the passkey list.
  • useRevokeSession: refresh the sessions list.
  • useRevokeMultiSession: refresh the device sessions list.
  • useUnlinkAccount: refresh the linked accounts list.

Tracking mutation state globally

All mutation keys start with "auth". The shared authMutationKeys factory in @better-auth-ui/core exposes these keys.

Use this factory instead of inline tuples. Call sites and mutation factories will then use the same keys:

import { authMutationKeys } from "@better-auth-ui/core"
import { useIsMutating } from "@tanstack/react-query"

const authPending = useIsMutating({ mutationKey: authMutationKeys.all })
const signInPending = useIsMutating({ mutationKey: authMutationKeys.signIn.all })
const emailSignInPending = useIsMutating({
  mutationKey: authMutationKeys.signIn.email
})

Each grouping (signIn, signUp, passkey, multiSession) exposes an all prefix so you can match a whole feature at once.

Match inside a MutationCache observer for global toasts or analytics:

import { authMutationKeys } from "@better-auth-ui/core"

new MutationCache({
  onError: (error, _vars, _ctx, mutation) => {
    if (mutation.options.mutationKey?.[0] === authMutationKeys.all[0]) {
      toast.error(error.message)
    }
  }
})

Escape hatch

Use useAuthMutation for a mutation endpoint that has no specific hook. Use useAuthQuery for a read endpoint.

import { authClient } from "@/lib/auth-client"
import { useAuthMutation } from "@better-auth-ui/react"

const { mutate } = useAuthMutation(
  authClient.emailOtp.sendVerificationOtp,
  ["auth", "emailOtp", "sendVerificationOtp"]
)

mutate({ email: "user@example.com", type: "sign-in" })

TypeScript infers variables from the authFn parameter. Required parameters prevent an empty mutate() call, while optional parameters permit it.

The factory adds throw: true to fetchOptions. Therefore, onError and error receive a BetterFetchError.

For shared mutation registration (useIsMutating, a global MutationCache observer, manual useMutation), import the endpoint's option factory from core and use it directly:

import { changePasswordOptions } from "@better-auth-ui/core"
import { useMutation } from "@tanstack/react-query"

const { mutate } = useMutation(changePasswordOptions(authClient))

For endpoints that already have a key in authMutationKeys, prefer it over an inline tuple so cache observers and useIsMutating checks line up.

Available mutations

Auth

Settings

Organization

Last updated on

On this page