MPUI-KIT

Phone input

A phone number field for a country. It groups digits as you type, detects the operator from the prefix, validates the length and gives you the E.164 number.

Preview

Saisissez votre numéro à 9 chiffres, sans l'indicatif du pays.

value
""
e164
null
valid
false
issue
—
locale

Try 6 51 23 45 67, paste +237 655 12 34 56, or use the operator option to require a specific operator.

Installation

npx shadcn@latest add @mpui-kit/phone-input

Requires the @mpui-kit namespace. See Getting started.

Usage

import { PhoneInput } from "@/components/mpui-kit/phone-input"

// Inside <MpuiKitProvider country={cm}>
const [phone, setPhone] = useState("")

<PhoneInput
  value={phone}
  onChange={(digits, details) => {
    setPhone(digits)          // "651234567"
    console.log(details.e164) // "+237651234567" once the number is valid
  }}
/>

Props

Props
PropTypeDefaultDescription
valuestring—The national number as digits, e.g. "651234567". An E.164 value such as "+237651234567" is also accepted. Makes the input controlled.
defaultValuestring—Initial value for an uncontrolled input.
onChange(value: string, details: PhoneValidation) => void—Called on every change with the national digits and the validation result. details.e164 is set once the number is valid.
countryCountryConfigMpuiKitProvider's countryCountry to use. Throws if neither this nor a provider is set.
locale"fr" | "en"MpuiKitProvider's locale, then “fr”Language of the label, hint and built-in errors.
labelstring | false“Phone number”Visible label. Pass false to hide it, and then give aria-label.
hintstring | falselength hintHelper text under the input. Pass false to hide it.
errorstring—Error message from your form library. Overrides the built-in error.
operatorstring—Id of the operator the number must belong to, e.g. "mtn". Only a known, different operator is rejected. A prefix that is not in the data is accepted.
requireOperatorbooleanfalseReject numbers whose prefix matches no known operator.
operatorLogosRecord<string, ReactNode>—Your own logos by operator id, shown in the badge instead of the color dot.
namestring—Adds a hidden field with this name that holds the E.164 number, so native form posts get it whatever the visible grouping.
classNamestring—Styles the outer wrapper. Use classNames.input for the text input.
classNames{ label, field, prefix, input, badge, hint, error }—Class names for parts of the field. Each part also has a data-slot attribute, such as phone-input-input.
...propsComponentProps<'input'>—Other input props (disabled, required, autoFocus, ref...) go to the text input.

react-hook-form and zod

Use Controller, not register: the input reports digits through onChange(value, details) rather than a DOM event. createPhoneSchema accepts what people type and outputs the E.164 number, with French or English messages.

Enter your 9-digit number, without the country code.

import { zodResolver } from "@hookform/resolvers/zod"
import { Controller, useForm } from "react-hook-form"
import { z } from "zod"

import { PhoneInput } from "@/components/mpui-kit/phone-input"
import { cm } from "@/lib/mpui-kit/countries/cm"
import { createPhoneSchema } from "@/lib/mpui-kit/phone-schema"

const schema = z.object({ phone: createPhoneSchema(cm, { locale: "en" }) })

const form = useForm<z.input<typeof schema>, unknown, z.output<typeof schema>>({
  resolver: zodResolver(schema),
  defaultValues: { phone: "" },
})

<form onSubmit={form.handleSubmit((data) => console.log(data.phone))}>
  <Controller
    control={form.control}
    name="phone"
    render={({ field, fieldState }) => (
      <PhoneInput
        ref={field.ref}
        name={field.name}
        value={field.value}
        onChange={(value) => field.onChange(value)}
        onBlur={field.onBlur}
        error={fieldState.error?.message}
      />
    )}
  />
</form>

// data.phone is the E.164 number: "+237651234567"

Install the schema helper with npx shadcn@latest add @mpui-kit/phone-schema. It needs zod, which the CLI installs for you.

OperatorBadge

The badge is a color dot and the operator name. MPUI-KIT ships no operator logos or brand assets. Pass your own through logo, or operatorLogos on the input, if you have the right to use them.

Props
PropTypeDefaultDescription
operator*OperatorConfig—Operator to show.
logoReactNode—Your own logo, shown instead of the color dot. MPUI-KIT ships no logos.
...propsComponentProps<'span'>—Any other span prop.

Notes

  • Prefix data is a hint. Operator prefixes are community-maintained and may be incomplete or out of date, so the badge can be missing or wrong. Never route money by it.
  • Accepts local numbers, +237 …, 00237 … and pasted numbers with spaces or dashes.
  • Errors appear when the field loses focus, not while typing, and are announced with role="alert". The detected operator is announced politely.
  • The caret stays where you expect it while digits are regrouped, and Backspace over a space deletes the digit before it.