# Kiltel

Kiltel is a lightweight country picker and phone-number formatter. It has no browser-side phone validator: the browser produces a consistent E.164 value and the server decides whether to accept it.

This avoids the old `intl-tel-input` validation failure that incorrectly rejected some valid India and Jamaica entries.

## How number normalization works

Kiltel synchronizes the following values after every edit (include the optional `country_iso` hidden input when you need the ISO country in your database):

| Field | Stored value | Jamaica example |
|---|---|---|
| Main phone input | E.164-style full number | `+18767425111` |
| `country_code` | Selected dialing prefix | `+1876` |
| `contact` | National subscriber digits only | `7425111` |
| `country_iso` | Selected ISO 3166-1 alpha-2 country | `JM` |

The country prefix is already visible in the main field. Enter only the digits that are not already shown:

- India: with `+91` selected, type `9876543210`, which becomes `+919876543210`.
- Jamaica: with `+1 876` selected, type `7425111`, which becomes `+18767425111`. Jamaica is `JM`; it shares the North American numbering plan but is not the United States.

Pasting an international number such as `+18767425111` also switches the picker to Jamaica automatically. Do not add a leading `0` for an Indian mobile number or repeat a prefix Kiltel has already supplied.

## CDN / plain HTML

Add the stylesheet and script:

```html
<link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/kiltel@3/kiltel.min.css">
<script defer src="https://cdn.jsdelivr.net/npm/kiltel@3/kiltel.min.js"></script>
```

Use this structure for each phone field. The hidden values must be inside the same `.kiltel-phone-wrap`.

```html
<div class="kiltel-phone-wrap">
  <input
    type="tel"
    class="form-control"
    name="phone"
    data-kilvish-tel
    data-kiltel-init="JM"
    placeholder="Enter the remaining phone digits"
    required
  >
  <input type="hidden" name="country_code">
  <input type="hidden" name="contact">
  <input type="hidden" name="country_iso">
</div>
```

Kiltel initializes on page load. For markup added later, call `window.kiltelInit()` after it is inserted.

The CDN build also exposes `window.kiltel.normalizePhone(value, country)`. It returns `{ country, countryCode, nationalNumber, e164, isPlausible }`.

## Next.js / React

Kiltel manipulates the DOM, so initialize it only from a client component.

### Install and add CSS

```bash
npm i kiltel
```

Import the CSS once, for example in `app/layout.js`:

```js
import 'kiltel/kiltel-next.css';
```

### App Router client component

```jsx
'use client';

import { useKiltel } from 'kiltel/kiltel-react';

export default function PhoneField() {
  useKiltel();

  return (
    <div className="kiltel-phone-wrap">
      <input
        type="tel"
        className="form-control"
        name="phone"
        data-kilvish-tel
        data-kiltel-init="IN"
        placeholder="Enter the remaining phone digits"
        required
      />
      <input type="hidden" name="country_code" />
      <input type="hidden" name="contact" />
      <input type="hidden" name="country_iso" />
    </div>
  );
}
```

### Pages Router or scoped manual initialization

```jsx
import { useEffect, useRef } from 'react';
import kiltel from 'kiltel/kiltel-next';
import 'kiltel/kiltel-next.css';

export default function PhoneField() {
  const wrapRef = useRef(null);

  useEffect(() => {
    kiltel.init({ scope: wrapRef.current });
    return () => kiltel.destroy({ scope: wrapRef.current });
  }, []);

  return (
    <div className="kiltel-phone-wrap" ref={wrapRef}>
      <input type="tel" className="form-control" name="phone" data-kilvish-tel data-kiltel-init="JM" />
      <input type="hidden" name="country_code" />
      <input type="hidden" name="contact" />
      <input type="hidden" name="country_iso" />
    </div>
  );
}
```

## Validate on the server

Do not trust hidden form fields and do not add `data-kiltel-validation`; that legacy attribute is no longer used. Normalize the submitted main input on the server, then persist the normalized values.

`kiltel/kiltel-next` can safely be imported by a Next.js route handler because `normalizePhone` does not access the DOM:

```js
import kiltel from 'kiltel/kiltel-next';

export async function POST(request) {
  const form = await request.formData();
  const phone = kiltel.normalizePhone(
    form.get('phone'),
    form.get('country_iso')
  );

  if (!phone?.isPlausible) {
    return Response.json({ error: 'Enter a complete phone number.' }, { status: 400 });
  }

  // Persist these values, not the client-supplied hidden fields.
  await saveUser({
    phoneE164: phone.e164,
    phoneCountry: phone.country,
    phoneCountryCode: phone.countryCode,
    phoneNationalNumber: phone.nationalNumber
  });

  return Response.json({ ok: true });
}
```

`isPlausible` verifies the normalized E.164 shape and length. If your product needs country-specific numbering-plan checks, perform them in that same server route with a maintained server-side telephone library or verification service. No format check can prove that the number is assigned; use SMS or voice verification for that.

## Attributes

| Attribute | Purpose |
|---|---|
| `data-kilvish-tel` | Enables Kiltel on the input. |
| `data-kiltel-init` | Initial ISO country or dialing prefix, for example `IN`, `JM`, or `+1876`. |
| `data-kilvish-only` | Comma-separated ISO country allow-list, such as `IN,JM,US`. |
| `data-kilvish-exclude` | Comma-separated ISO country block-list. |
| `data-kilvish-preferred` | Comma-separated ISO countries pinned at the top of the list. |

## License

MIT
