Skip to content

🧩 Slots

<VueTelNumInput> exposes slots to customize the prefix button, the input, the search bar, and list rows.

Important:

  • Prefix, input and search slots are plain slots.
  • List item slots (item:*) are scoped slots and expose row data (see section below).
  • You can still use your app state (e.g., v-model object) or global CSS if you need additional data inside slots.
  • If you replace the internal <input> or search <input>, you must keep the model and events in sync.

Prefix button (prefix:*)

These slots render inside the prefix button (country selector trigger) at the left of the input.

Slot namePurposeNotes
prefix:beforeContent before everything in the buttonPlain slot
prefix:flagCustom flag elementPlain slot
prefix:codeCustom dialing code text (+421)Plain slot
prefix:countryNameCustom country labelPlain slot
prefix:chevronCustom chevron / indicatorPlain slot; not rendered if list.hidden is true
prefix:afterContent after everything in the buttonPlain slot

Example:

vue
<VueTelNumInput v-model="model">
  <template #prefix:flag>
    <img :src="`/flags/${model.iso}.svg`" alt="" width="16" height="12" />
  </template>

  <template #prefix:code>
    <span class="muted">{{ model.code }}</span>
  </template>

  <template #prefix:chevron>
    <svg width="12" height="12" viewBox="0 0 16 16"><path d="..." /></svg>
  </template>
</VueTelNumInput>

Input (input)

Replace the entire input field.

Slot namePurposeNotes
inputReplace the internal <input>You must: (1) sync v-model yourself, (2) forward focus and blur if needed

Minimal example (manual sync):

vue
<VueTelNumInput v-model="model">
  <template #input>
    <input
      :value="model.value"
      @input="e => model.value = { ...model.value, value: (e.target as HTMLInputElement).value }"
      @focus="$emit('focus')"
      @blur="$emit('blur')"
      :placeholder="`Number for ${model.name} (${model.code})`"
    />
  </template>
</VueTelNumInput>

Search area

You can replace the whole search row or just parts of it.

Slot namePurposeNotes
body:searchReplace the entire search container rowWraps icon + input + before/after; plain slot
search:beforeContent before the search icon/inputPlain slot
search:iconReplace the search iconPlain slot
search:inputReplace the search <input>Plain slot; you must bind to model.search yourself
search:afterContent after the search inputPlain slot

Example (custom icon + input):

vue
<VueTelNumInput v-model="model">
  <template #search:icon>
    <span style="margin-left:12px">🔎</span>
  </template>

  <template #search:input>
    <input
      v-model="model.search"
      placeholder="Type a country or code…"
    />
  </template>
</VueTelNumInput>

Full override:

vue
<VueTelNumInput v-model="model">
  <template #body:search>
    <div class="my-search">
      <input v-model="model.search" placeholder="Find country…" />
      <button @click="model.search = ''">Clear</button>
    </div>
  </template>
</VueTelNumInput>

List rows (item:*)

All item:* slots are scoped slots. They receive row data so you can fully control how each country is rendered.

Depending on the specific slot, you get some of the following props:

  • data – raw country object
  • index – row index in the filtered list
  • iso – country ISO code (e.g. US)
  • countryName – display name for the country
  • countryCode – dialing code with + (e.g. +421)
  • selectedtrue if the row is currently selected
Slot namePurposeNotes
item:beforeContent before row contentsScoped slot with data
item:flagReplace the row flagScoped slot with data
item:codeReplace the row dialing codeScoped slot with data
item:countryNameReplace the row country nameScoped slot with data
item:afterContent after row contentsScoped slot with data

Example (simple embellishments with row data):

vue
<VueTelNumInput v-model="model">
  <template #item:before="{ selected }">
    <span v-if="selected">✔</span>
    <span v-else>•</span>
  </template>

  <template #item:flag="{ iso }">
    <img :src="`/flags/${iso}.svg`" alt="" width="16" height="12" />
  </template>

  <template #item:code="{ countryCode }">
    <span class="muted">{{ countryCode }}</span>
  </template>
</VueTelNumInput>

Note: Only item:* slots are scoped and receive per-row data; prefix, input and search-related slots remain plain slots.

Tips & Caveats

  • If you hide the dropdown via list.hidden = true, prefix:chevron won’t render (as in the component).
  • When replacing the internal <input> or search <input>:
    • Update model.value / model.search yourself.
    • Forward @focus / @blur if you rely on those events.
    • Respect input.maxLength, input.required, etc., if your UI should mimic defaults.
  • For purely visual tweaks, prefer CSS (via component CSS variables) over slots to avoid re-implementing behavior.