Disabled Options
Prevent certain options from being selected using the disabled prop on SelectOption. Disabled options remain visible in the dropdown but cannot be clicked or selected via keyboard navigation.
Static Disabled Options
Set the disabled prop directly on individual SelectOption components. Disabled options are skipped during keyboard navigation and cannot be selected by click.
<script setup lang="ts">
import { ref } from 'vue'
import {
SelectRoot,
SelectControl,
SelectInput,
SelectContent,
SelectOption,
} from 'vue-superselect'
const selected = ref<string | null>(null)
interface Drink {
id: string
name: string
disabled: boolean
}
const drinks: Drink[] = [
{ id: 'coffee', name: 'Coffee', disabled: false },
{ id: 'tea', name: 'Tea', disabled: false },
{ id: 'espresso', name: 'Espresso', disabled: true },
{ id: 'latte', name: 'Latte', disabled: false },
{ id: 'matcha', name: 'Matcha', disabled: true },
{ id: 'cocoa', name: 'Hot Cocoa', disabled: false },
]
</script>
<template>
<div class="do-demo">
<SelectRoot
v-model="selected"
:items="drinks"
label-key="name"
value-key="id"
>
<SelectControl class="do-control">
<SelectInput placeholder="Choose a drink..." class="do-input" />
</SelectControl>
<SelectContent class="do-content">
<SelectOption
v-for="drink in drinks"
:key="drink.id"
v-slot="{ selected: isSelected, active, disabled }"
:value="drink.id"
:label="drink.name"
:disabled="drink.disabled"
class="do-option"
:class="{
'do-option--selected': isSelected,
'do-option--active': active,
'do-option--disabled': disabled,
}"
>
{{ drink.name }}
<span v-if="disabled" class="do-badge">Unavailable</span>
</SelectOption>
</SelectContent>
</SelectRoot>
<p v-if="selected" class="do-result">Selected: <strong>{{ selected }}</strong></p>
<p class="demo-note">This styling is for demos only. The library ships zero CSS</p>
</div>
</template>
<style scoped>
.do-demo {
max-width: 320px;
}
.do-control {
display: flex;
align-items: center;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 0.5rem 0.75rem;
background: var(--vp-c-bg);
transition: border-color 0.2s;
}
.do-control:focus-within {
border-color: var(--vp-c-brand-1);
box-shadow: 0 0 0 2px var(--vp-c-brand-soft);
}
.do-input {
width: 100%;
border: none;
outline: none;
font-size: 0.9375rem;
background: transparent;
color: var(--vp-c-text-1);
}
.do-input::placeholder {
color: var(--vp-c-text-3);
}
.do-content {
position: absolute;
top: 100%;
left: 0;
right: 0;
margin-top: 4px;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
max-height: 200px;
overflow-y: auto;
z-index: 50;
padding: 4px;
}
.do-option {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0.75rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.9375rem;
color: var(--vp-c-text-1);
transition: background-color 0.15s;
}
.do-option--active {
background-color: var(--vp-c-brand-soft);
}
.do-option--selected {
font-weight: 600;
color: var(--vp-c-brand-1);
}
.do-option--disabled {
opacity: 0.45;
cursor: not-allowed;
}
.do-badge {
font-size: 0.6875rem;
padding: 0.125rem 0.375rem;
border-radius: 4px;
background: var(--vp-c-bg-soft);
color: var(--vp-c-text-3);
}
.do-result {
margin-top: 0.75rem;
font-size: 0.875rem;
color: var(--vp-c-text-2);
}
</style>Step by Step
<script setup>
import { ref } from 'vue'
import { SelectRoot, SelectControl, SelectInput, SelectContent, SelectOption } from 'vue-superselect'
const selected = ref(null)
const drinks = [
{ id: 'coffee', name: 'Coffee', disabled: false },
{ id: 'espresso', name: 'Espresso', disabled: true },
{ id: 'latte', name: 'Latte', disabled: false },
]
</script>
<template>
<SelectRoot v-model="selected" :items="drinks" label-key="name" value-key="id">
<SelectControl>
<SelectInput placeholder="Choose a drink..." />
</SelectControl>
<SelectContent>
<SelectOption
v-for="drink in drinks"
:key="drink.id"
:value="drink.id"
:label="drink.name"
:disabled="drink.disabled"
>
{{ drink.name }}
</SelectOption>
</SelectContent>
</SelectRoot>
</template>The :disabled prop is a boolean on SelectOption. When true:
- The option is visible but not clickable
- Keyboard navigation (Arrow Up/Down) skips over it
- The
disabledscoped slot prop istrue - The option receives
aria-disabled="true"automatically
Conditional Disabling
Compute the disabled state from your option data. In this example, products with zero stock are automatically disabled.
<script setup lang="ts">
import { ref } from 'vue'
import {
SelectRoot,
SelectControl,
SelectInput,
SelectContent,
SelectOption,
} from 'vue-superselect'
const selected = ref<string | null>(null)
interface Product {
id: string
name: string
stock: number
}
const products: Product[] = [
{ id: 'keyboard', name: 'Mechanical Keyboard', stock: 12 },
{ id: 'mouse', name: 'Ergonomic Mouse', stock: 0 },
{ id: 'monitor', name: '4K Monitor', stock: 3 },
{ id: 'headset', name: 'Wireless Headset', stock: 0 },
{ id: 'webcam', name: 'HD Webcam', stock: 7 },
{ id: 'mic', name: 'USB Microphone', stock: 0 },
]
</script>
<template>
<div class="dc-demo">
<SelectRoot
v-model="selected"
:items="products"
label-key="name"
value-key="id"
>
<SelectControl class="dc-control">
<SelectInput placeholder="Choose a product..." class="dc-input" />
</SelectControl>
<SelectContent class="dc-content">
<SelectOption
v-for="product in products"
:key="product.id"
v-slot="{ selected: isSelected, active, disabled }"
:value="product.id"
:label="product.name"
:disabled="product.stock === 0"
class="dc-option"
:class="{
'dc-option--selected': isSelected,
'dc-option--active': active,
'dc-option--disabled': disabled,
}"
>
<span>{{ product.name }}</span>
<span v-if="product.stock === 0" class="dc-stock dc-stock--out">Out of stock</span>
<span v-else class="dc-stock">{{ product.stock }} left</span>
</SelectOption>
</SelectContent>
</SelectRoot>
<p v-if="selected" class="dc-result">Selected: <strong>{{ selected }}</strong></p>
<p class="demo-note">This styling is for demos only. The library ships zero CSS</p>
</div>
</template>
<style scoped>
.dc-demo {
max-width: 360px;
}
.dc-control {
display: flex;
align-items: center;
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
padding: 0.5rem 0.75rem;
background: var(--vp-c-bg);
transition: border-color 0.2s;
}
.dc-control:focus-within {
border-color: var(--vp-c-brand-1);
box-shadow: 0 0 0 2px var(--vp-c-brand-soft);
}
.dc-input {
width: 100%;
border: none;
outline: none;
font-size: 0.9375rem;
background: transparent;
color: var(--vp-c-text-1);
}
.dc-input::placeholder {
color: var(--vp-c-text-3);
}
.dc-content {
position: absolute;
top: 100%;
left: 0;
right: 0;
margin-top: 4px;
background: var(--vp-c-bg);
border: 1px solid var(--vp-c-divider);
border-radius: 8px;
box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1), 0 2px 4px -2px rgba(0, 0, 0, 0.1);
max-height: 240px;
overflow-y: auto;
z-index: 50;
padding: 4px;
}
.dc-option {
display: flex;
align-items: center;
justify-content: space-between;
padding: 0.5rem 0.75rem;
border-radius: 4px;
cursor: pointer;
font-size: 0.9375rem;
color: var(--vp-c-text-1);
transition: background-color 0.15s;
}
.dc-option--active {
background-color: var(--vp-c-brand-soft);
}
.dc-option--selected {
font-weight: 600;
color: var(--vp-c-brand-1);
}
.dc-option--disabled {
opacity: 0.45;
cursor: not-allowed;
}
.dc-stock {
font-size: 0.75rem;
color: var(--vp-c-text-3);
}
.dc-stock--out {
color: var(--vp-c-danger-1, #e53e3e);
}
.dc-result {
margin-top: 0.75rem;
font-size: 0.875rem;
color: var(--vp-c-text-2);
}
</style>Computed Disabled State
The key line is :disabled="product.stock === 0". Any expression that evaluates to a boolean works:
<SelectOption
v-for="product in products"
:key="product.id"
:value="product.id"
:label="product.name"
:disabled="product.stock === 0"
>Common patterns for conditional disabling:
| Pattern | Expression |
|---|---|
| Out of stock | :disabled="item.stock === 0" |
| User permissions | :disabled="!item.canAccess" |
| Dependency | :disabled="!selectedCategory" |
| Already used | :disabled="usedIds.includes(item.id)" |
Styling Disabled Options
Using Scoped Slot Props
The disabled prop is exposed as a scoped slot prop on SelectOption:
<SelectOption
v-slot="{ selected, active, disabled }"
:disabled="item.disabled"
>
<span :class="{ 'opacity-50': disabled }">{{ item.name }}</span>
</SelectOption>Using Data Attributes
Disabled options automatically receive data-disabled="true", which you can target with CSS:
[data-disabled="true"] {
opacity: 0.45;
cursor: not-allowed;
}This approach works well for pure CSS styling without scoped slot props.
Accessibility
Disabled options are fully accessible out of the box:
aria-disabled="true"is set automatically- Screen readers announce the option as disabled
- Keyboard navigation skips disabled options (Arrow Up/Down jump to the next enabled option)
- Disabled options do not respond to mouse clicks
No additional ARIA configuration is needed.