BETTER-AUTH-UI.
GitHub ↗

Phone Number

Add phone verification-code and password sign-in to Solid and Zaidan applications.

The Solid/Zaidan registry item adds phone verification-code and password sign-in, phone password recovery, and verified phone-number management. 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

src/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.

Update the schema and client

Generate or migrate the Better Auth schema so the user model has nullable phoneNumber and phoneNumberVerified fields. Keep phoneNumber unique.

src/lib/auth-client.ts
import { createAuthClient } from "@better-auth-ui/solid"
import { phoneNumberClient } from "better-auth/client/plugins"

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

Add the registry item

bunx --bun shadcn@latest add https://better-auth-ui.com/r/solid/phone-number.json

Register the UI plugin

src/components/providers.tsx
import { phoneNumberPlugin } from "@/lib/auth/phone-number-plugin"

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

Allow the plugin route segments

src/routes/auth/$path.tsx
import { viewPaths } from "@better-auth-ui/core"
import { phoneNumberPlugin } from "@/lib/auth/phone-number-plugin"

const validAuthPathSegments = 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.

Passwordless phone verification bypasses Better Auth 2FA. Phone number and password sign-in still follows the configured second-factor flow.

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

Options and localization

Prop

Type

Prop

Type

The copied components use useSendPhoneNumberOtp and the other Solid phone-number mutation hooks. See the Better Auth phone-number plugin for server behavior and advanced options.

Last updated on

On this page