Command Palette

Search for a command...

Phone Input

A phone input field with country code dropdown.

Enter a phone number

""
"use client";

import { Controller, useForm } from "react-hook-form";
import { isValidPhoneNumber } from "react-phone-number-input";
import { zodResolver } from "@hookform/resolvers/zod";
import { styled } from "styled-system/jsx";
import { z } from "zod";
import { toast } from "@/hooks/use-toast";
import { Button } from "@/components/ui/button";
import { Field, FieldDescription, FieldError, FieldLabel } from "@/components/ui/field";
import { PhoneInput } from "@/components/ui/phone-input";

const formSchema = z.object({
  phone: z.string().refine(isValidPhoneNumber, { message: "Invalid phone number" }),
});

export default function PhoneInputDemo() {
  const form = useForm<z.infer<typeof formSchema>>({
    resolver: zodResolver(formSchema),
    defaultValues: {
      phone: "",
    },
  });

  const onSubmit = form.handleSubmit((data) => {
    toast({
      title: "You submitted the following values:",
      description: (
        <styled.pre
          css={{ mt: "2", w: "340px", rounded: "md", bg: "slate.950", p: "4", borderWidth: "1px" }}
        >
          <styled.code css={{ color: "white" }}>{JSON.stringify(data, null, 2)}</styled.code>
        </styled.pre>
      ),
    });
  });

  return (
    <styled.form
      onSubmit={onSubmit}
      css={{ display: "flex", flexDir: "column", alignItems: "flex-start", spaceY: "8" }}
    >
      <Controller
        control={form.control}
        name="phone"
        render={({ field, fieldState }) => (
          <Field
            data-invalid={fieldState.invalid}
            css={{ display: "flex", flexDir: "column", alignItems: "flex-start" }}
          >
            <FieldLabel css={{ textAlign: "left" }}>Phone Number</FieldLabel>
            <PhoneInput placeholder="Enter a phone number" {...field} />
            <FieldDescription css={{ textAlign: "left" }}>Enter a phone number</FieldDescription>
            <FieldError>{fieldState.error?.message}</FieldError>
          </Field>
        )}
      />
      <pre>
        <styled.code css={{ color: "muted.fg" }}>
          {JSON.stringify(form.watch("phone"), null, 2)}
        </styled.code>
      </pre>
      <Button type="submit">Submit</Button>
    </styled.form>
  );
}

About

The Phone Input component is built on top of React Phone Number Input. Thanks to omeralpi for implementing this component.

Installation

npx nore-ui-cli@latest add phone-input

Usage

import { PhoneInput } from "@/components/ui/phone-input";
const [value, setValue] = React.useState("");

return <InputPhone value={value} onChange={setValue} />;

Examples

Setting default country

"use client";

import { useState } from "react";
import { PhoneInput } from "@/components/ui/phone-input";

export default function PhoneInputDefaultCountry() {
  const [phoneNumber, setPhoneNumber] = useState("");

  return (
    <PhoneInput
      value={phoneNumber}
      onChange={setPhoneNumber}
      defaultCountry="ID"
      placeholder="Enter a phone number"
    />
  );
}

Internationalization

"use client";

import { useState } from "react";
import ja from "react-phone-number-input/locale/ja";
import { PhoneInput } from "@/components/ui/phone-input";

export default function PhoneInputInternationalization() {
  const [phoneNumber, setPhoneNumber] = useState("");

  return (
    <PhoneInput
      value={phoneNumber}
      onChange={setPhoneNumber}
      labels={ja}
      placeholder="電話番号を入力してください"
    />
  );
}

Force international format

"use client";

import { useState } from "react";
import { PhoneInput } from "@/components/ui/phone-input";

export default function PhoneInputInternational() {
  const [phoneNumber, setPhoneNumber] = useState("");

  return (
    <PhoneInput value={phoneNumber} onChange={setPhoneNumber} international defaultCountry="ID" />
  );
}

Force national format

"use client";

import { useState } from "react";
import { PhoneInput } from "@/components/ui/phone-input";

export default function PhoneInputNational() {
  const [phoneNumber, setPhoneNumber] = useState("");

  return (
    <PhoneInput
      value={phoneNumber}
      onChange={setPhoneNumber}
      international={false}
      defaultCountry="ID"
      placeholder="Enter a phone number"
    />
  );
}

initialValueFormat

"use client";

import { useState } from "react";
import { PhoneInput } from "@/components/ui/phone-input";

export default function PhoneInputInitial() {
  const [phoneNumber, setPhoneNumber] = useState("");

  return (
    <PhoneInput
      value={phoneNumber}
      onChange={setPhoneNumber}
      initialValueFormat="national"
      placeholder="Enter a phone number"
    />
  );
}

Formatting value

National:
International:
Country code:
"use client";

import { useState } from "react";
import {
  Country,
  formatPhoneNumber,
  formatPhoneNumberIntl,
  getCountryCallingCode,
} from "react-phone-number-input";
import { styled } from "styled-system/jsx";
import { PhoneInput } from "@/components/ui/phone-input";

export default function PhoneInputFormattingValue() {
  const [country, setCountry] = useState<Country>();
  const [phoneNumber, setPhoneNumber] = useState("");

  return (
    <div>
      <PhoneInput
        value={phoneNumber}
        onChange={setPhoneNumber}
        onCountryChange={setCountry}
        placeholder="Enter a phone number"
      />
      <styled.div css={{ mt: "4", spaceY: "2", textStyle: "sm" }}>
        <div>National: {phoneNumber && formatPhoneNumber(phoneNumber)}</div>
        <div>International: {phoneNumber && formatPhoneNumberIntl(phoneNumber)}</div>
        <div>Country code: {country && getCountryCallingCode(country)}</div>
      </styled.div>
    </div>
  );
}