Skip to content

NbSelect is a custom dropdown that replaces the native <select>. It shares the same design language as NbTextInput: two variants (default / fluid), error/warning states, multi-select support, and full keyboard navigation.

vue
<template>
  <NbSelect
    v-model="locale"
    label="Locale"
    :options="[
      { label: 'English', value: 'en' },
      { label: 'Portuguese', value: 'pt' },
      { label: 'Spanish', value: 'es' },
    ]"
  />
</template>

Variants ​

Default ​

The label sits above the field. Helper/error/warning messages appear below.

Fluid ​

The label is rendered inside the field at the top. Useful in dense forms or inline editors.

vue
<template>
  <NbSelect
    v-model="value"
    variant="fluid"
    label="Status"
    :options="statusOptions"
  />
</template>

Validation states ​

Disabled ​

Multi-select ​

Set :multiple="true" and bind to an array. The trigger displays the item label when 1 is selected, a comma-separated list for 2, and N selected for 3 or more.

vue
<template>
  <NbSelect
    v-model="selected"
    label="Target locales"
    :options="localeOptions"
    :multiple="true"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue'
const selected = ref<string[]>([])
</script>

Creatable ​

Set :creatable="true" to show a text input at the bottom of the dropdown. When the user types a value and presses Enter, the create event fires with the entered string. You can then add it to your options array.

vue
<template>
  <NbSelect
    v-model="locale"
    label="Locale"
    :options="options"
    creatable
    create-placeholder="New locale..."
    @create="onCreate"
  />
</template>

<script setup lang="ts">
import { ref } from 'vue'

const locale = ref('en')
const options = ref([
  { label: 'English', value: 'en' },
  { label: 'Portuguese', value: 'pt' },
])

function onCreate(value: string) {
  options.value.push({ label: value, value: value.toLowerCase() })
}
</script>

Props ​

PropTypeDefaultDescription
modelValuestring | number | Array<string|number> | nullnullCurrent selection (v-model)
optionsISelectOption[][]Array of options
multiplebooleanfalseAllow multiple selections
creatablebooleanfalseShow a text input for creating new options
createPlaceholderstring'Add new...'Placeholder for the create input
variant'default' | 'fluid''default'Layout variant
labelstring-Label text
placeholderstring'Select…'Placeholder when nothing is selected
helperstring-Persistent helper text below the field
errorstring-Error message — triggers error state
warningstring-Warning message — triggers warning state
disabledbooleanfalseDisables the select
requiredbooleanfalseMarks the field as required (adds *)
idstringautoNative input id (auto-generated if omitted)
namestring-Native form name

An icon per option ​

Give an option an icon when people recognise it by mark before they read the word: a platform, a provider, a file kind. It appears in the list and on the closed select, so the two cannot drift apart.

vue
<script setup lang="ts">
const options = [
  { label: 'LinkedIn', value: 'linkedin', icon: 'linkedin-logo' },
  { label: 'Instagram', value: 'instagram', icon: 'instagram-logo' },
  { label: 'X', value: 'x', icon: 'x-logo' },
  { label: 'Anywhere', value: 'generic' },
]
</script>

<template>
  <NbSelect v-model="platform" label="Platform" :options="options" />
</template>

Three things worth knowing:

  • An option without an icon is a plain row, not a gap where a mark should be, so a list can mix the two.
  • A multiple select shows no mark on the trigger. Its value is a count, and one mark beside "2 selected" would say less than the label alone.
  • The name is resolved at runtime. With the glyph catalogue off, register the names you pass (registerIcons), the same as anywhere else an icon name is computed rather than written in a template.

For a row this cannot express, an avatar and two lines of text for example, the option and value slots are still there.

Option interface ​

typescript
interface ISelectOption {
  label: string
  value: string | number
  disabled?: boolean
  /** Artwork before the label, in the list and on the closed select. */
  icon?: string
}

Events ​

EventPayloadDescription
update:modelValuestring | number | Array<string|number> | nullEmitted on every selection change
changesame as aboveAlso emitted on every change
createstringEmitted when the user submits a new value via the create input (requires creatable)

Exposed ​

typescript
const selectRef = ref<InstanceType<typeof NbSelect>>()
selectRef.value?.open() // programmatically open the dropdown
selectRef.value?.close() // programmatically close the dropdown

Keyboard navigation ​

KeyAction
Enter / SpaceOpen dropdown / select item
↑ / ↓Move highlight through options
Escape / TabClose dropdown

Accessibility ​

  • The trigger button announces aria-expanded, aria-haspopup="listbox", and aria-controls pointing at the dropdown.
  • Each option has role="option" with aria-selected and aria-disabled.
  • The for/id association is handled automatically.