<!-- https://svgrid.com/docs/help/ui-components/sv-phone-input/ - SvGrid documentation as markdown. Index of every page: https://svgrid.com/llms.txt -->

# SvPhoneInput

A country dial-code selector plus a national number field that emits an
E.164-style string.

`SvPhoneInput` pairs a flag + dial-code picker with a national number field and
combines them into a single `+<dial><digits>` value. Picking a country reformats
the number for that country's convention, and `onChange` also hands you the parsed
`parts` (country, dial, national, valid, complete) for your own validation. It is
the styled renderer over the headless `createPhoneInput` core. Its label / hint /
error chrome comes from [SvField](https://svgrid.com/docs/help/ui-components/sv-field/).

Related: [SvMaskedInput](https://svgrid.com/docs/help/ui-components/sv-masked-input/) · [SvTextInput](https://svgrid.com/docs/help/ui-components/sv-text-input/) · [Inputs overview](https://svgrid.com/docs/help/ui-components/inputs/)

## Installation

Add it with the CLI - this drops a ready-to-edit `SvPhoneInput` starter into your app:

<div data-docs-add="add phone-input"></div>

Prefer to see it first? `npx @svgrid/ui try phone-input` opens it in a throwaway sandbox - no project needed.

Or install the package and import it directly. `SvPhoneInput` ships free in
`@svgrid/grid` and is **part of the grid's editor kit** - the same control SvGrid mounts when you edit a matching cell:

<div data-docs-install="@svgrid/grid"></div>

The examples on this page import from `@svgrid/grid`:

```svelte
<script lang="ts">
  import { SvPhoneInput } from '@svgrid/grid'

  // The bound value behind each example below.
  let phone = $state('')
</script>
```

```ts
import { SvPhoneInput } from '@svgrid/grid'
```

## Example

> Live demo: Phone input - https://svgrid.com/demos/303-phone-input/

```svelte
<script lang="ts">
  import { SvPhoneInput } from '@svgrid/grid'
  let phone = $state('')
</script>

<SvPhoneInput label="Mobile" country="US" bind:value={phone} />
```

## Props

`SvPhoneInput` extends the shared `SvEditorProps` (`disabled`, `readonly`,
`required`, `invalid`, `error`, `label`, `hint`, `size`, `dir`, `name`, `id`,
`ariaLabel`) and adds:

| Prop          | Type                                              | Default          | Description                                  |
| ------------- | ------------------------------------------------- | ---------------- | -------------------------------------------- |
| `value`       | `string`                                          | `''`             | The `+<dial><digits>` string.                |
| `onChange`    | `(value: string, parts: PhoneParts) => void`      | -                | Fires with the combined value and parsed parts. |
| `country`     | `string`                                          | `US`             | Default country ISO code.                    |
| `placeholder` | `string`                                          | `Phone number`   | National-field placeholder.                  |
| `messages`    | `Partial<PhoneMessages>`                          | -                | Override the country selector label.         |
| `block`       | `boolean`                                         | `false`          | Stretch to the container width.              |
| `width`       | `number`                                          | `250`            | Control width in px (ignored when `block`).  |

`PhoneParts` is `{ country; dial; national; valid; complete }`. `PhoneMessages`
is `{ country }`.

The box, size, invalid state and focus ring are owned by [SvField](https://svgrid.com/docs/help/ui-components/sv-field/)'s
shared `frame` chrome; the country selector is a compact flag + dial-code chip in
the field's leading slot.

## Examples

### Read the parsed parts

`onChange`'s second argument gives you validity and completeness without
re-parsing the string yourself:

```svelte
<SvPhoneInput
  bind:value={phone}
  onChange={(v, parts) => { phone = v; valid = parts.valid }}
/>
```

### Default country

Set `country` to the ISO code that fits your audience; the dial code and
formatting follow it until the user picks another:

```svelte
<SvPhoneInput country="GB" bind:value={phone} />
```

### Required field that blocks on an invalid number

Capture the parsed `parts` from `onChange` and drive the error off `parts.valid`,
so the field only complains once the user has typed something incomplete:

```svelte
<script lang="ts">
  import { SvPhoneInput } from '@svgrid/grid'
  let phone = $state('')
  let parts = $state<{ valid: boolean } | null>(null)
  const showError = $derived(parts != null && !parts.valid)
</script>

<SvPhoneInput
  label="Mobile"
  country="US"
  required
  bind:value={phone}
  onChange={(v, p) => { phone = v; parts = p }}
  invalid={showError}
  error={showError ? 'Enter a complete phone number' : undefined}
/>
```

Tip: `parts` also carries `country`, `dial`, `national`, and `complete`, so you
can store the pieces separately without re-parsing the combined string yourself.

## Accessibility

- The country picker is a native `<select>` overlaid on the flag/dial display,
  so it uses the OS picker and keyboard, with a `country` `aria-label` from
  `messages`.
- `label`, `hint`, and `error` are wired via [SvField](https://svgrid.com/docs/help/ui-components/sv-field/); `required`
  and `invalid` add `aria-required` / `aria-invalid`.
- The country list lives in `countries.ts`; extend it for the full set your app
  needs.

## More examples

### Phone input - headless

createPhoneInput drives SvPhoneInput and a custom country picker + national field; dial-code / E.164 parsing from the core.

> Live demo: Phone input - headless - https://svgrid.com/demos/268-headless-phoneinput/

## Sizes

Every control takes the same three sizes, so a dense toolbar and a roomy form can share components.

```svelte
<script lang="ts">
  import { SvPhoneInput } from '@svgrid/grid'

  let phone = $state('')
</script>

<SvPhoneInput bind:value={phone} size="sm" />
<SvPhoneInput bind:value={phone} size="md" />
<SvPhoneInput bind:value={phone} size="lg" />
```

## In a form

The shared field props behave the same on every editor: `label` names it, `hint` explains it, and `error` plus `invalid` mark it - which is why a validated form does not need per-component handling.

```svelte
<script lang="ts">
  import { SvPhoneInput } from '@svgrid/grid'

  let phone = $state('')
</script>

<SvPhoneInput
  bind:value={phone}
  label="Label"
  hint="A short hint"
  required
/>

<SvPhoneInput
  bind:value={phone}
  label="Label"
  error="Something is wrong"
  invalid
/>
```

## See also

- [Inputs overview](https://svgrid.com/docs/help/ui-components/inputs/) - the whole input family at a glance.
- [SvMaskedInput](https://svgrid.com/docs/help/ui-components/sv-masked-input/) - the general pattern-mask field.
- [SvTextInput](https://svgrid.com/docs/help/ui-components/sv-text-input/) - the base single-line field.

---

SvGrid is the Svelte 5 data grid (`npm install @svgrid/grid`, MIT). This page: https://svgrid.com/docs/help/ui-components/sv-phone-input/ . All docs: https://svgrid.com/llms.txt
