# PhoneField Guides

## Patterns

### Country-Aware Contact Form

When to use:

- the product stores international phone numbers

Composition notes:

- keep the country select visible
- normalize values at submit time
- show a compact value in the trigger (here the flag) and the full country name in the list

Trade-offs:

- better international correctness
- more complex form behavior than a plain text input

```tsx
import {
  Label,
  PhoneField,
  PhoneFieldInput,
  PhoneFieldSelect,
  PhoneFieldSelectCombobox,
  PhoneFieldSelectHiddenSelect,
  PhoneFieldSelectIcon,
  PhoneFieldSelectListbox,
  PhoneFieldSelectOption,
  PhoneFieldSelectValue,
} from '@vega-ui/react';
import { FlagDE, FlagFR, FlagGBUKM, FlagIN, FlagJP, FlagUS } from '@vega-ui/icons';
import { useId, useState } from 'react';

const countries = [
  { code: 'US', name: 'United States', callingCode: '+1', Flag: FlagUS },
  { code: 'GB', name: 'United Kingdom', callingCode: '+44', Flag: FlagGBUKM },
  { code: 'DE', name: 'Germany', callingCode: '+49', Flag: FlagDE },
  { code: 'FR', name: 'France', callingCode: '+33', Flag: FlagFR },
  { code: 'IN', name: 'India', callingCode: '+91', Flag: FlagIN },
  { code: 'JP', name: 'Japan', callingCode: '+81', Flag: FlagJP },
] as const;

export default function SelectWithCountryOptionsAndIcons() {
  const id = useId();
  const [country, setCountry] = useState<(typeof countries)[number]>(countries[1]);

  const onChangeCode = (code: string) => {
    const found = countries.find((item) => item.code === code);
    if (found) setCountry(found);
  };

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-3)', width: '100%', maxWidth: 340 }}>
      <Label htmlFor={id}>Phone number</Label>
      <PhoneField code={country.code} onChangeCode={onChangeCode}>
        <PhoneFieldSelect>
          <PhoneFieldSelectHiddenSelect name='country' />
          <PhoneFieldSelectCombobox aria-label={`Country: ${country.name}`}>
            <PhoneFieldSelectValue>
              <country.Flag width={20} height={15} aria-hidden='true' />
            </PhoneFieldSelectValue>
            <PhoneFieldSelectIcon />
          </PhoneFieldSelectCombobox>
          <PhoneFieldSelectListbox>
            {countries.map(({ code, name, callingCode, Flag }) => (
              <PhoneFieldSelectOption key={code} value={code} label={name}>
                <span style={{ display: 'flex', alignItems: 'center', gap: 'var(--spacing-6)' }}>
                  <Flag width={20} height={15} aria-hidden='true' />
                  {name}
                </span>
                <span style={{ opacity: 0.7 }}>{callingCode}</span>
              </PhoneFieldSelectOption>
            ))}
          </PhoneFieldSelectListbox>
        </PhoneFieldSelect>
        <PhoneFieldInput id={id} name='phone' autoComplete='tel' />
      </PhoneField>
    </div>
  );
}
```

### Strict Mask Entry

When to use:

- the entry format should stay tightly constrained

Composition notes:

- `strictMask` is on by default: the calling code of `code` is fixed and only its digits are accepted
- retest caret movement and paste behavior

Trade-offs:

- cleaner formatted input
- less forgiving editing

```tsx
import { Label, PhoneField, PhoneFieldInput } from '@vega-ui/react';
import { useId } from 'react';

export default function SimpleIsoBasedPhoneMask() {
  const id = useId();

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-3)', width: '100%', maxWidth: 280 }}>
      <Label htmlFor={id}>Mobile number</Label>
      <PhoneField code='US'>
        <PhoneFieldInput id={id} name='phone' autoComplete='tel' placeholder='+1 201 555-0123' />
      </PhoneField>
    </div>
  );
}
```

### Lenient Paste Flow

When to use:

- users often paste phone numbers from external systems

Composition notes:

- use `strictMask={false}`
- a pasted number with another calling code switches the country, and `onChangeCode` reports it
- normalize at the feature boundary

Trade-offs:

- better paste tolerance
- weaker in-field formatting guarantees

```tsx
import {
  Label,
  PhoneField,
  PhoneFieldInput,
  PhoneFieldSelect,
  PhoneFieldSelectCombobox,
  PhoneFieldSelectIcon,
  PhoneFieldSelectListbox,
  PhoneFieldSelectOption,
  PhoneFieldSelectValue,
  Text,
} from '@vega-ui/react';
import { useId, useState } from 'react';

export default function NonStrictMaskForLenientPasteBehavior() {
  const id = useId();
  const [country, setCountry] = useState('US');

  return (
    <div style={{ display: 'flex', flexDirection: 'column', gap: 'var(--spacing-3)', width: '100%', maxWidth: 340 }}>
      <Label htmlFor={id}>Customer phone</Label>
      <PhoneField defaultCode='US' strictMask={false} onChangeCode={setCountry}>
        <PhoneFieldSelect>
          <PhoneFieldSelectCombobox aria-label='Country'>
            <PhoneFieldSelectValue />
            <PhoneFieldSelectIcon />
          </PhoneFieldSelectCombobox>
          <PhoneFieldSelectListbox>
            <PhoneFieldSelectOption value='US'>US</PhoneFieldSelectOption>
            <PhoneFieldSelectOption value='GB'>GB</PhoneFieldSelectOption>
            <PhoneFieldSelectOption value='DE'>DE</PhoneFieldSelectOption>
            <PhoneFieldSelectOption value='FR'>FR</PhoneFieldSelectOption>
            <PhoneFieldSelectOption value='JP'>JP</PhoneFieldSelectOption>
          </PhoneFieldSelectListbox>
        </PhoneFieldSelect>
        <PhoneFieldInput id={id} autoComplete='tel' />
      </PhoneField>
      <Text size={2} style={{ color: 'var(--label-secondary)', marginBlockStart: 'var(--spacing-2)' }}>
        Paste +33 6 12 34 56 78 or +81 90-1234-5678: the country switches to match. Detected: {country}
      </Text>
    </div>
  );
}
```

## Comparison

### Quick Decision Rule

Choose `PhoneField` when the phone value and country selection should behave as one coordinated field.

### `PhoneField` vs `TextField`

Use `PhoneField` when:

- country-aware phone formatting matters
- the UX needs a mask or country picker

Use `TextField` when:

- the value is just plain text input

### `PhoneField` vs `Select` + `TextField`

Use `PhoneField` when:

- country selection and phone formatting should stay coupled
- one canonical phone-entry contract should be reused across the app

Use separate controls when:

- the product intentionally separates country selection from phone entry
- masking, validation, and country changes should not share one field lifecycle

### Choose This Component When

- mask behavior matters
- country selection changes validation or formatting
- the product treats phone entry as one field family

### Do Not Choose This Component When

- phone formatting is irrelevant
- a plain text field is enough
- country choice should be fully independent from the input

## Troubleshooting

### Country Change Does Not Reformat The Number Correctly

#### Symptom

Changing the selected country leaves the visible number in an invalid or stale format.

#### Likely Cause

Country state and validation logic are not synchronized in the parent flow.

#### How To Verify

- switch countries with an existing input value
- compare displayed and stored value behavior

#### Fix

- treat country and value as one integration flow
- normalize after country changes

### Paste Behavior Feels Too Strict

#### Symptom

Users paste a number, but the field rejects or mangles the input unexpectedly.

#### Likely Cause

`strictMask` is too strict for the real input source.

#### How To Verify

- test paste flows with realistic phone strings

#### Fix

- consider `strictMask={false}` for lenient flows
- normalize the canonical value after input

### Country Select Looks Correct But Form Semantics Are Weak

#### Symptom

The UI works visually, but the country selection is missing from normal form flows.

#### Likely Cause

`PhoneFieldSelectHiddenSelect` is missing.

#### How To Verify

- inspect native form submission behavior

#### Fix

- render `PhoneFieldSelectHiddenSelect` inside the select composition
