PhoneInput

The PhoneInput component provides a flexible way to capture telephone numbers with built-in masking and formatting.

Basic

Controlled PhoneInput example that surfaces both raw digits and the formatted display.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

 

import

 

{

 

Block

,

 

Card

,

 

Code

,

 

PhoneInput

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

raw

,

setRaw

]

 

=

 

useState

(

''

)

;

  

const

 

[

formatted

,

setFormatted

]

 

=

 

useState

(

''

)

;

  

const

 

[

e164

,

setE164

]

 

=

 

useState

(

''

)

;

  

const

 

[

complete

,

setComplete

]

 

=

 

useState

(

false

)

;

 

  

return

 

(

    

<Block

fullWidth

>

      

<Text

 

weight

=

"semibold"

>

Basic

phone input

</Text

>

      

<Text

 

size

=

"sm"

 

color

=

"secondary"

>

        

Controlled

phone field showing the raw national digits

,

the formatted display

        value

,

and the submittable

E

.

164

form

.

      

</Text

>

      

<PhoneInput

        

label

=

"Phone number"

        

value

=

{

raw

}

        

onChange

=

{

(

rawDigits

,

formattedDisplay

,

meta

)

 

=>

 

{

          

setRaw

(

rawDigits

)

;

          

setFormatted

(

formattedDisplay

)

;

          

setE164

(

meta

.

e164

)

;

          

setComplete

(

meta

.

isComplete

)

;

        

}

}

        

country

=

"US"

        showCountryCode

      

/>

      

<Card

 

variant

=

"outline"

 

p

=

"sm"

>

        

<Block

>

          

<Text

 

size

=

"xs"

 

color

=

"secondary"

>

            

Current

values

          

</Text

>

          

<Code

 

size

=

"sm"

>

{

JSON

.

stringify

(

{

raw

,

formatted

,

e164

,

complete

}

,

 

null

,

 

2

)

}

</Code

>

        

</Block

>

      

</Card

>

    

</Block

>

  

)

;

}

International

Compare auto-detected formats with a manual international configuration.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

 

import

 

{

 

Block

,

 

Card

,

 

Code

,

 

PhoneInput

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

autoDetectValue

,

setAutoDetectValue

]

 

=

 

useState

(

''

)

;

  

const

 

[

autoDetectE164

,

setAutoDetectE164

]

 

=

 

useState

(

''

)

;

  

const

 

[

autoDetectCountry

,

setAutoDetectCountry

]

 

=

 

useState

(

'US'

)

;

  

const

 

[

intlValue

,

setIntlValue

]

 

=

 

useState

(

''

)

;

  

const

 

[

intlFormatted

,

setIntlFormatted

]

 

=

 

useState

(

''

)

;

 

  

return

 

(

    

<Block

fullWidth

>

      

<Text

 

weight

=

"semibold"

>

International

detection

</Text

>

      

<Text

 

size

=

"sm"

 

color

=

"secondary"

>

        

With

autoDetect

,

an explicit

+

prefix picks the country

:

 

type

or paste

        

+

447911123456

and the mask

,

dial code and

E

.

164

output follow along

.

 

A

        recognized dial code is stripped on paste either way

,

so a full international

        

number

 

never

overflows the national mask

.

      

</Text

>

 

      

<Block

>

        

<PhoneInput

          

label

=

"Auto-detect from a + prefix"

          

value

=

{

autoDetectValue

}

          

onChange

=

{

(

raw

,

_formatted

,

meta

)

 

=>

 

{

            

setAutoDetectValue

(

raw

)

;

            

setAutoDetectE164

(

meta

.

e164

)

;

          

}

}

          

defaultCountry

=

"US"

          

onCountryChange

=

{

setAutoDetectCountry

}

          autoDetect

          showCountryCode

          

placeholder

=

"Try +447911123456 or +33123456789"

        

/>

 

        

<PhoneInput

          

label

=

"Manual international"

          

country

=

"INTL"

          

value

=

{

intlValue

}

          

onChange

=

{

(

raw

,

formatted

)

 

=>

 

{

            

setIntlValue

(

raw

)

;

            

setIntlFormatted

(

formatted

)

;

          

}

}

          

showCountryCode

=

{

false

}

          

placeholder

=

"Enter any international number"

        

/>

      

</Block

>

 

      

<Card

 

variant

=

"outline"

 

p

=

"sm"

>

        

<Block

>

          

<Text

 

size

=

"xs"

 

color

=

"secondary"

>

            

Values

          

</Text

>

          

<Code

 

size

=

"sm"

>

            

{

JSON

.

stringify

(

              

{

                autoDetect

:

 

{

                  country

:

autoDetectCountry

,

                  raw

:

autoDetectValue

,

                  e164

:

autoDetectE164

                

}

,

                international

:

 

{

raw

:

intlValue

,

formatted

:

intlFormatted

}

              

}

,

              

null

,

              

2

            

)

}

          

</Code

>

        

</Block

>

      

</Card

>

    

</Block

>

  

)

;

}

Country Picker

Let the user pick the country from the dial-code prefix, remasking the number in place.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

 

import

 

{

 

Block

,

 

Card

,

 

Code

,

 

PhoneInput

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

country

,

setCountry

]

 

=

 

useState

(

'US'

)

;

  

const

 

[

raw

,

setRaw

]

 

=

 

useState

(

''

)

;

  

const

 

[

e164

,

setE164

]

 

=

 

useState

(

''

)

;

 

  

return

 

(

    

<Block

fullWidth

>

      

<Text

 

weight

=

"semibold"

>

Country

picker

</Text

>

      

<Text

 

size

=

"sm"

 

color

=

"secondary"

>

        

With

selectableCountry the dial

-

code prefix becomes a dropdown

.

 

Changing

the

        country remasks the digits already entered instead of clearing them

,

and the

        

E

.

164

value is rebuilt against the

new

dial code

.

      

</Text

>

 

      

<PhoneInput

        

label

=

"Phone number"

        selectableCountry

        

country

=

{

country

}

        

onCountryChange

=

{

setCountry

}

        

value

=

{

raw

}

        

onChange

=

{

(

rawDigits

,

_formatted

,

meta

)

 

=>

 

{

          

setRaw

(

rawDigits

)

;

          

setE164

(

meta

.

e164

)

;

        

}

}

      

/>

 

      

<Card

 

variant

=

"outline"

 

p

=

"sm"

>

        

<Block

>

          

<Text

 

size

=

"xs"

 

color

=

"secondary"

>

            

Current

values

          

</Text

>

          

<Code

 

size

=

"sm"

>

{

JSON

.

stringify

(

{

country

,

raw

,

e164

}

,

 

null

,

 

2

)

}

</Code

>

        

</Block

>

      

</Card

>

    

</Block

>

  

)

;

}

Country Formats

Showcase of built-in country presets with their localized masks and raw digit output.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

 

import

 

{

 

Block

,

 

Card

,

 

Code

,

 

PhoneInput

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

us

,

setUs

]

 

=

 

useState

(

''

)

;

  

const

 

[

uk

,

setUk

]

 

=

 

useState

(

''

)

;

  

const

 

[

fr

,

setFr

]

 

=

 

useState

(

''

)

;

  

const

 

[

br

,

setBr

]

 

=

 

useState

(

''

)

;

 

  

return

 

(

    

<Block

fullWidth

>

      

<Text

 

weight

=

"semibold"

>

Country

formatting

</Text

>

      

<Text

 

size

=

"sm"

 

color

=

"secondary"

>

        

Compare

built

-

in masks

for

several countries

.

 

Each

input stores digits only

while

rendering a localized format

.

      

</Text

>

 

      

<Block

>

        

<PhoneInput

          

label

=

"United States"

          

country

=

"US"

          

value

=

{

us

}

          

onChange

=

{

(

raw

)

 

=>

 

setUs

(

raw

)

}

          showCountryCode

        

/>

        

<PhoneInput

          

label

=

"United Kingdom"

          

country

=

"GB"

          

value

=

{

uk

}

          

onChange

=

{

(

raw

)

 

=>

 

setUk

(

raw

)

}

          showCountryCode

        

/>

        

<PhoneInput

          

label

=

"France"

          

country

=

"FR"

          

value

=

{

fr

}

          

onChange

=

{

(

raw

)

 

=>

 

setFr

(

raw

)

}

          showCountryCode

        

/>

        

<PhoneInput

          

label

=

"Brazil"

          

country

=

"BR"

          

value

=

{

br

}

          

onChange

=

{

(

raw

)

 

=>

 

setBr

(

raw

)

}

          showCountryCode

        

/>

      

</Block

>

 

      

<Card

 

variant

=

"outline"

 

p

=

"sm"

>

        

<Block

>

          

<Text

 

size

=

"xs"

 

color

=

"secondary"

>

            

Raw

digit values

          

</Text

>

          

<Code

 

size

=

"sm"

>

{

JSON

.

stringify

(

{

us

,

uk

,

fr

,

br

}

,

 

null

,

 

2

)

}

</Code

>

        

</Block

>

      

</Card

>

    

</Block

>

  

)

;

}

Mask Visibility

Demonstrates showing or hiding the country code prefix while preserving raw digits.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

 

import

 

{

 

Block

,

 

PhoneInput

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

withCountryCode

,

setWithCountryCode

]

 

=

 

useState

(

''

)

;

  

const

 

[

withoutCountryCode

,

setWithoutCountryCode

]

 

=

 

useState

(

''

)

;

 

  

return

 

(

    

<Block

fullWidth

>

      

<Text

 

weight

=

"semibold"

>

Country

code visibility

</Text

>

      

<Text

 

size

=

"sm"

 

color

=

"secondary"

>

        

Toggle

the country prefix

while

keeping the same underlying digits

.

      

</Text

>

 

      

<Block

>

        

<Block

>

          

<PhoneInput

            

label

=

"With country code"

            

value

=

{

withCountryCode

}

            

onChange

=

{

(

raw

)

 

=>

 

setWithCountryCode

(

raw

)

}

            

country

=

"US"

            showCountryCode

          

/>

          

<Text

 

size

=

"xs"

 

color

=

"secondary"

>

            

Raw

digits

:

 

{

withCountryCode

||

 

'—'

}

          

</Text

>

        

</Block

>

 

        

<Block

>

          

<PhoneInput

            

label

=

"Without country code"

            

value

=

{

withoutCountryCode

}

            

onChange

=

{

(

raw

)

 

=>

 

setWithoutCountryCode

(

raw

)

}

            

country

=

"US"

            

showCountryCode

=

{

false

}

          

/>

          

<Text

 

size

=

"xs"

 

color

=

"secondary"

>

            

Raw

digits

:

 

{

withoutCountryCode

||

 

'—'

}

          

</Text

>

        

</Block

>

      

</Block

>

    

</Block

>

  

)

;

}

Validation

Length-based validation for US and international formats with inline messaging.

Loading demo…

import

 

{

useMemo

,

useState

}

 

from

 

'react'

;

 

import

 

{

 

Block

,

 

PhoneInput

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

usRaw

,

setUsRaw

]

 

=

 

useState

(

''

)

;

  

const

 

[

usFormatted

,

setUsFormatted

]

 

=

 

useState

(

''

)

;

  

const

 

[

internationalRaw

,

setInternationalRaw

]

 

=

 

useState

(

''

)

;

  

const

 

[

internationalFormatted

,

setInternationalFormatted

]

 

=

 

useState

(

''

)

;

 

  

const

isValidUs

=

 

useMemo

(

(

)

 

=>

usRaw

.

length

===

 

10

,

 

[

usRaw

]

)

;

  

const

isValidInternational

=

 

useMemo

(

    

(

)

 

=>

internationalRaw

.

length

>=

 

7

 

&&

internationalRaw

.

length

<=

 

15

,

    

[

internationalRaw

]

  

)

;

 

  

return

 

(

    

<Block

fullWidth

>

      

<Text

 

weight

=

"semibold"

>

Validation

states

</Text

>

      

<Text

 

size

=

"sm"

 

color

=

"secondary"

>

        

Surface

validation messages based on raw digit counts

for

domestic and international numbers

.

      

</Text

>

 

      

<Block

>

        

<Block

>

          

<PhoneInput

            

label

=

"US phone (10 digits required)"

            

value

=

{

usRaw

}

            

onChange

=

{

(

raw

,

formatted

)

 

=>

 

{

              

setUsRaw

(

raw

)

;

              

setUsFormatted

(

formatted

)

;

            

}

}

            

country

=

"US"

            showCountryCode

            

error

=

{

usRaw

.

length

>

 

0

 

&&

 

!

isValidUs

?

 

'Enter a 10-digit US phone number'

 

:

 

undefined

}

          

/>

          

<Text

 

size

=

"xs"

 

color

=

{

isValidUs

||

usRaw

.

length

===

 

0

 

?

 

'success'

 

:

 

'error'

}

>

            

{

usRaw

.

length

===

 

0

              

?

 

'Enter a phone number'

              

:

isValidUs

                

?

 

`✓ ${usFormatted}`

                

:

 

`${usRaw.length}/10 digits entered`

}

          

</Text

>

        

</Block

>

 

        

<Block

>

          

<PhoneInput

            

label

=

"International phone (7-15 digits)"

            

value

=

{

internationalRaw

}

            

onChange

=

{

(

raw

,

formatted

)

 

=>

 

{

              

setInternationalRaw

(

raw

)

;

              

setInternationalFormatted

(

formatted

)

;

            

}

}

            

defaultCountry

=

"INTL"

            autoDetect

            showCountryCode

            

error

=

{

              internationalRaw

.

length

>

 

0

 

&&

 

!

isValidInternational

                

?

 

'International numbers should be 7-15 digits'

                

:

 

undefined

            

}

          

/>

          

<Text

            

size

=

"xs"

            

color

=

{

isValidInternational

||

internationalRaw

.

length

===

 

0

 

?

 

'success'

 

:

 

'error'

}

          

>

            

{

internationalRaw

.

length

===

 

0

              

?

 

'Enter an international phone number'

              

:

isValidInternational

                

?

 

`✓ ${internationalFormatted}`

                

:

 

'Adjust to 7-15 digits'

}

          

</Text

>

        

</Block

>

      

</Block

>

    

</Block

>

  

)

;

}

Advanced Masking

Custom mask patterns for international formats and extension fields.

Loading demo…

import

 

{

useState

}

 

from

 

'react'

;

 

import

 

{

 

Block

,

 

PhoneInput

,

 

Text

 

}

 

from

 

'@platform-blocks/react-ui-library'

;

 

export

 

function

 

Demo

(

)

 

{

  

const

 

[

intlRaw

,

setIntlRaw

]

 

=

 

useState

(

''

)

;

  

const

 

[

intlFormatted

,

setIntlFormatted

]

 

=

 

useState

(

''

)

;

  

const

 

[

extensionRaw

,

setExtensionRaw

]

 

=

 

useState

(

''

)

;

  

const

 

[

extensionFormatted

,

setExtensionFormatted

]

 

=

 

useState

(

''

)

;

 

  

return

 

(

    

<Block

fullWidth

>

      

<Text

 

weight

=

"semibold"

>

Advanced

masking

</Text

>

      

<Text

 

size

=

"sm"

 

color

=

"secondary"

>

        

Apply

custom mask patterns to control formatting

for

international numbers and extension fields

.

      

</Text

>

 

      

<Block

>

        

<PhoneInput

          

label

=

"International format"

          

value

=

{

intlRaw

}

          

onChange

=

{

(

raw

,

formatted

)

 

=>

 

{

            

setIntlRaw

(

raw

)

;

            

setIntlFormatted

(

formatted

)

;

          

}

}

          

autoDetect

=

{

false

}

          

showCountryCode

=

{

false

}

          

mask

=

"+00 (000) 000-0000"

          

placeholder

=

"+44 (7911) 123-456"

        

/>

        

<Text

 

size

=

"xs"

 

color

=

"secondary"

>

          

Raw

digits

:

 

{

intlRaw

||

 

'—'

}

        

</Text

>

        

<Text

 

size

=

"xs"

 

color

=

"secondary"

>

          

Formatted

:

 

{

intlFormatted

||

 

'—'

}

        

</Text

>

      

</Block

>

 

      

<Block

>

        

<PhoneInput

          

label

=

"North America with extension"

          

value

=

{

extensionRaw

}

          

onChange

=

{

(

raw

,

formatted

)

 

=>

 

{

            

setExtensionRaw

(

raw

)

;

            

setExtensionFormatted

(

formatted

)

;

          

}

}

          

autoDetect

=

{

false

}

          

showCountryCode

=

{

false

}

          

mask

=

"000-000-0000 x0000"

          

placeholder

=

"555-123-4567 x1234"

        

/>

        

<Text

 

size

=

"xs"

 

color

=

"secondary"

>

          

Raw

digits

:

 

{

extensionRaw

||

 

'—'

}

        

</Text

>

        

<Text

 

size

=

"xs"

 

color

=

"secondary"

>

          

Formatted

:

 

{

extensionFormatted

||

 

'—'

}

        

</Text

>

      

</Block

>

    

</Block

>

  

)

;

}

Scan to open this page on your phonereact-ui-library.com/components/PhoneInput

</>

react-ui-library

100+ accessible, themeable components that work seamlessly across iOS, Android, and Web.

A Platform Blocks product.