Configure current-address approval, new-address verification, and account settings without mistaking a successful request for a completed change.

Changing an account's email affects its sign-in identity and recovery address. BTST's account settings provide the form; Better Auth decides when the new address replaces the old one. Configure and test that server policy before exposing the control.
This guide targets @btst/better-auth-ui@2.0.1 with @btst/stack@3.1.2 and better-auth@1.6.16. It extends an existing BTST auth integration. The example requires approval from a verified current address followed by verification of the new address.
Merge the following options into your existing Better Auth server configuration. Preserve any other properties in user and emailVerification when merging; a shallow spread of a whole replacement object can discard them.
import type { BetterAuthOptions } from "better-auth";
import { after } from "next/server";
import { sendEmail } from "@/lib/email";
export const emailChangePolicy = {
user: {
changeEmail: {
enabled: true,
sendChangeEmailConfirmation: async ({ user, newEmail, url }) => {
after(async () => {
await sendEmail({
to: user.email,
subject: "Approve your email change",
text: `Approve changing your address to ${newEmail}: ${url}`,
});
});
},
},
},
emailVerification: {
sendVerificationEmail: async ({ user, url }) => {
after(async () => {
await sendEmail({
to: user.email,
subject: "Verify your email address",
text: `Verify this address: ${url}`,
});
});
},
},
} satisfies BetterAuthOptions;
sendEmail represents your existing server-only delivery function. This Next.js example uses after within the auth request to keep delivery attached to the request lifecycle. On another framework or host, use its supported background-work mechanism. Observe delivery failures without recording verification links or tokens.
The pinned change-email handler sends the first message to the current address only when that address is verified and the confirmation callback is configured. Without that callback, the ordinary flow verifies the new address directly. The upstream account documentation describes these choices.
For a currently unverified account, this configuration sends verification to the new address without treating the old address as proven. It leaves updateEmailWithoutVerification disabled. Do not add that option just to make the form appear to save immediately.
Mount the account plugin alongside the auth plugin. With the usual /p mount, account settings are at /p/account/settings. Merge this fragment into your existing overrides.auth:
import type { AuthPluginOverrides } from "@btst/better-auth-ui/client";
export const emailChangeUI = {
changeEmail: true,
emailVerification: true,
} satisfies Partial<AuthPluginOverrides>;
changeEmail controls the account form. emailVerification makes the account settings able to show its resend-verification control. Neither enables the server endpoint or delivers a message. The released card calls authClient.changeEmail with the new address and the current pathname as its callback.
This guide uses verification links. If the same application also uses the email-code verification flow, test how its server plugin overrides and account resend control interact with email changes. A working registration code flow alone does not verify this link-based address-change sequence.
For an account whose current address is verified:
Repeat with an unverified account, an expired link, and an address already belonging to another account. The pinned endpoint can return a generic successful response for an already-used address without changing ownership. Avoid converting that response into an email-availability indicator.
The companion's success wording depends partly on the session's previous verification state. In particular, an unverified account can display an updated message while the server is still waiting for new-address verification. Inspect the resulting user record and delivery sequence when testing; a successful request or toast is not proof of a completed change.
Application records should refer to the stable user ID where possible. Updating an email is not a request to create a second user, transfer an organization, or merge another account. If your application copies email into billing or notification systems, define when those copies update and how failed synchronization is retried.
Keep the generated verification URL intact, preserve trusted-origin checks, and never accept arbitrary post-verification redirects from untrusted input. For an account that has lost access to its verified current address, use your deliberate recovery process rather than silently bypassing the approval requirement.
The snippets were type-checked, and the pinned handler was tested with an in-memory adapter and stubbed mail delivery. Those checks cover the two-message transition and rejected or incomplete cases; they do not verify real inbox delivery, an OAuth provider's profile updates, or a full application UI flow. See the BTST auth documentation for provider wiring and the password-recovery guide for the separate recovery path.