BETTER-AUTH. UI
Plugins

Phone Number

Add phone verification-code and password sign-in, recovery, and verified phone management.

The HeroUI phone-number plugin contributes a phone sign-in route, password recovery views, and a verified phone-number card for account settings. Its country selector formats national input as the user types, validates it, and sends an E.164 number to Better Auth.

Setup

Configure Better Auth and your SMS provider

lib/auth.ts
import { betterAuth } from "better-auth"
import { phoneNumber } from "better-auth/plugins"

export const auth = betterAuth({
  plugins: [
    phoneNumber({
      otpLength: 6,
      requireVerification: true,
      sendOTP: ({ phoneNumber, code }) => {
        void sms.send({ to: phoneNumber, body: `Your code is ${code}` })
      },
      sendPasswordResetOTP: ({ phoneNumber, code }) => {
        void sms.send({ to: phoneNumber, body: `Your reset code is ${code}` })
      }
    })
  ]
})

Keep server-side validation as a trust boundary even though the UI normalizes numbers to E.164. Do not log codes in production. Better Auth recommends dispatching the SMS without waiting for the provider response.

Update the schema

Use your normal Better Auth schema generation or migration flow. The user model needs nullable phoneNumber and phoneNumberVerified fields, with phoneNumber kept unique.

Add the client plugin

lib/auth-client.ts
import { createAuthClient } from "better-auth/react"
import { phoneNumberClient } from "better-auth/client/plugins"

export const authClient = createAuthClient({
  plugins: [phoneNumberClient()]
})

Register the HeroUI plugin

components/providers.tsx
import { AuthProvider } from "@better-auth-ui/heroui"
import { phoneNumberPlugin } from "@better-auth-ui/heroui/plugins"

<AuthProvider
  authClient={authClient}
  navigate={navigate}
  plugins={[
    phoneNumberPlugin({
      signIn: true,
      passwordSignIn: true,
      passwordReset: true,
      changePhoneNumber: true
    })
  ]}
>
  {children}
</AuthProvider>

Allow the new route segments

import { viewPaths } from "@better-auth-ui/core"
import { phoneNumberPlugin } from "@better-auth-ui/heroui/plugins"

const validAuthPaths = new Set([
  ...Object.values(viewPaths.auth),
  ...Object.values(phoneNumberPlugin().viewPaths.auth)
])

Flow options

UI optionDefaultServer requirement
signIntruesendOTP
passwordSignInfalseA password credential
passwordResetfalsesendPasswordResetOTP
changePhoneNumbertruesendOTP
otpLength6Must match Better Auth otpLength

Use defaultCountry, countries, and locale to control the selector. Supply an adapter when your application needs different formatting or validation rules.

When password sign-in reports PHONE_NUMBER_NOT_VERIFIED, the UI switches to the code step. Better Auth sends that verification code automatically.

Passwordless phone verification is not a second factor. Better Auth applies 2FA to phone number and password sign-in, but not to passwordless verification.

Enable Better Auth signUpOnVerification to create an account after it verifies an unknown number. If verification requires more user fields, use a custom view.

Options and localization

Prop

Type

Prop

Type

The implementation uses the React phone-number mutations. See the Better Auth phone-number plugin for all server options.

Last updated on

On this page