Component
Vue Listbox Component
<dom-listbox>A styled select-like list without text entry.
Playground
Try every prop live
Listbox playground
Use this when users choose from visible options and should not type arbitrary text.
Properties
Control props
Selected option value.
Available options.
Arrow key direction.
Field props
Optional ID override. By default parent forms derive the input ID from the field path using underscores.
Local field name. Parent forms derive the full field path and native HTML name from the form hierarchy.
Visible field label.
Optional helper copy below the field.
Placeholder shown when the control is empty.
Validation errors for this field.
Validators
No validators yet.
Validators attached to this field. Use functions in Vue code, or serializable records such as { name: "minLength", props: { min: 2 } } in generated schemas.
Render default field chrome, or hide chrome while keeping form state wiring.
npm install @getdom/studio<script setup>
import '@getdom/studio/style.css';
import { reactive } from 'vue';
import { DomListbox } from '@getdom/studio';
const data = reactive({
"modelValue": "medium",
"id": "",
"name": "",
"label": "",
"description": "",
"placeholder": "",
"required": false,
"disabled": false,
"readOnly": false,
"invalid": false,
"errors": [],
"visible": true,
"validators": [],
"validateOnBlur": true,
"chrome": "field",
"options": [
{
"value": "small",
"label": "Small"
},
{
"value": "medium",
"label": "Medium"
},
{
"value": "large",
"label": "Large"
}
],
"orientation": "vertical"
});
</script>
<template>
<DomListbox
v-bind="data"
@update:modelValue="data.modelValue = $event"
/>
</template>Demo
Custom option markup
The option slot can render richer labels while v-model remains the option value.
Selected value: team
npm install @getdom/studio<script setup>
import '@getdom/studio/style.css';
import { ref } from 'vue';
import { DomListbox } from '@getdom/studio';
const value = ref('team');
const options = [
{ value: 'solo', label: 'Solo', description: 'For small personal projects.' },
{ value: 'team', label: 'Team', description: 'Shared workspace and billing.' },
{ value: 'enterprise', label: 'Enterprise', description: 'SSO, audit logs, and support.' },
];
</script>
<template>
<div class="grid w-full max-w-md gap-3">
<DomListbox v-model="value" :options="options">
<template #option="{ option }">
<span class="block font-medium">{{ option.label }}</span>
<span class="block text-xs opacity-75">{{ option.description }}</span>
</template>
</DomListbox>
<p class="text-xs text-muted-fg">Selected value: <code class="text-canvas-fg">{{ value }}</code></p>
</div>
</template>
Demo
Rich access options
Use listboxes for visible rich choices where each option benefits from badges, descriptions, or risk context.
Selected value: editor
npm install @getdom/studio<script setup>
import '@getdom/studio/style.css';
import { ref } from 'vue';
import { DomBadge, DomListbox, DomStatusPill } from '@getdom/studio';
const access = ref('editor');
const options = [
{
value: 'viewer',
label: 'Viewer',
description: 'Can read content and export approved reports.',
tone: 'neutral',
badge: 'Low risk',
},
{
value: 'editor',
label: 'Editor',
description: 'Can create, update, and submit work for review.',
tone: 'info',
badge: 'Recommended',
},
{
value: 'admin',
label: 'Admin',
description: 'Can manage billing, members, integrations, and security.',
tone: 'warning',
badge: 'Sensitive',
},
];
</script>
<template>
<div class="grid w-full max-w-md gap-3">
<DomListbox v-model="access" label="Access level" :options="options">
<template #option="{ option }">
<span class="flex items-start justify-between gap-4">
<span class="min-w-0">
<span class="flex items-center gap-2">
<span class="font-medium">{{ option.label }}</span>
<DomStatusPill :tone="option.tone" size="sm">{{ option.badge }}</DomStatusPill>
</span>
<span class="mt-1 block text-xs leading-5 text-muted-fg">{{ option.description }}</span>
</span>
<DomBadge v-if="option.value === access" tone="success" size="sm">Selected</DomBadge>
</span>
</template>
</DomListbox>
<p class="text-xs text-muted-fg">Selected value: <code class="text-canvas-fg">{{ access }}</code></p>
</div>
</template>
Reference
Props
Control props
| Name | Type | TS | Default | Description |
|---|---|---|---|---|
modelValue | string | number | string | '' | Selected option value. |
options*ts | array | Array< | — | Available options. |
orientation | 'vertical' | 'horizontal' | string | 'vertical' | Arrow key direction. |
Field props
| Name | Type | TS | Default | Description |
|---|---|---|---|---|
id | string | string | '' | Optional ID override. By default parent forms derive the input ID from the field path using underscores. |
name | string | string | '' | Local field name. Parent forms derive the full field path and native HTML name from the form hierarchy. |
label | string | string | '' | Visible field label. |
description | string | string | '' | Optional helper copy below the field. |
placeholder | string | string | '' | Placeholder shown when the control is empty. |
required | boolean | boolean | false | Mark the field as required. |
disabled | boolean | boolean | false | Disable field interaction. |
readOnly | boolean | boolean | false | Show the value but prevent editing. |
invalid | boolean | boolean | false | Mark the field invalid. |
errorsts | array | object | string | Array< | [] | Validation errors for this field. |
visible | boolean | boolean | true | Show or hide the field. |
validators | array | Array<unknown> | [] | Validators attached to this field. Use functions in Vue code, or serializable records such as { name: "minLength", props: { min: 2 } } in generated schemas. |
validateOnBlur | boolean | boolean | true | Run validators when the field loses focus. |
chrome | 'field' | 'none' | false | string | 'field' | Render default field chrome, or hide chrome while keeping form state wiring. |
Auto-generated from Listbox.props and inline _edit hints.
Events
| Name | Payload | Description |
|---|---|---|
| @update:modelValue | ( | Emitted when selection changes. |
| @select | ({ option, value }) | Emitted with the full selected option. |
| @focus | — | — |
| @blur | — | — |
Names auto-detected from defineEmits and source emit() calls; payload and description from __doc.events when present.