Skip to main content
Vuetify0 v1.0 is here
Vuetify0 Logo
Theme
Mode
Palettes
Accessibility
Vuetify One
Sign in to Vuetify One

Access premium tools across the Vuetify ecosystem — Bin, Play, Studio, and more.

Not a subscriber? See what's included

BuSelect

Edit this page
Report a Bug
Open issues
Copy Markdown

Renders elementIntermediateAug 24, 2026

Bulma’s .select wrapping a real <select>. Native options go in the default slot; v-model is a string, or a string array when multiple is set.

Note

Reference: Select on bulma.io↗︎ — classes and visual variants. This page is the JavaScript.

Usage

This is a styled native select, not a listbox. Put <option> (and <optgroup>) elements in the default slot the way you would in HTML. Color, size, rounded, multiple and loading land on the .select wrapper.

v-model is string | string[] | undefined. Bind a shallowRef for a single select and a ref([]) for multiple. An uncontrolled multiple select is healed to [] at setup so Vue does not warn on mount.

<script setup lang="ts">
  import { BuControl, BuField, BuLabel, BuSelect } from '@paper/bulma'

  import { shallowRef } from 'vue'

  const choice = shallowRef<string | undefined>()
</script>

<template>
  <BuField>
    <BuLabel>Country</BuLabel>

    <BuControl>
      <BuSelect v-model="choice">
        <option>Select dropdown</option>
        <option>With options</option>
      </BuSelect>
    </BuControl>
  </BuField>
</template>

Anatomy

vue
<script setup lang="ts">
  import { BuSelect } from '@paper/bulma'
</script>

<template>
  <BuSelect />
</template>

Composed on v0

BuSelect calls createInput for validation, form registration, the generated id and aria-invalid. The element is a native <select> inside div.select.

It does not wrap v0’s Select compound. That compound is a listbox — role="listbox", virtual focus, a popover of items — and none of that is an element Bulma’s .select CSS can paint. A non-native BuSelect would get none of your stylesheet. This is the Ruling 2 deviation: Tier 1 form controls wrap the native where Bulma’s CSS demands it.

Fallthrough is split. class and style merge onto the wrapper; every other attribute (aria-*, autofocus, data-*, …) lands on the native select. inheritAttrs is false, so nothing double-applies.

The markup you know

The Bulma tab is the markup published on bulma.io↗︎, captured verbatim in the conformance fixture. The Vue tab is the component that renders it. The conformance suite diffs the two on every test run — element for element, class for class.

html
<div class="select">
  <select>
    <option>Select dropdown</option>
    <option>With options</option>
  </select>
</div>

Icons wrap the select, they do not go on it. A BuControl with icons="left" around BuSelect is the documented pairing; the icon size tracks the select size.

loading is the exception to the input family’s rule: is-loading goes on the .select wrapper here, where it replaces the dropdown arrow, not on a wrapping control.

Examples

Multiple

multiple does two things at once: is-multiple on the wrapper, and the native multiple attribute on the select. v-model becomes a string[]. Bind a ref([]) — arrays get ref, primitives get shallowRef.

rows is the native size attribute, not a CSS size. It is how many option rows are visible; it pairs with multiple the way Bulma documents it. CSS size (is-small and friends) is the size prop, and it still lands on the wrapper.

A failed value paints is-danger on the wrapper, same as color. There is no listbox keyboard map to document — the UA owns arrow keys, typeahead and space. If you need filtering, multi-select chips, or a trigger that is not a native select, that is v0’s Select (or Combobox), and it will not look like Bulma’s.

Props

PropTypeDefaultDescription
v-modelstring | string[] | undefinedSelected value. Array when multiple
color'primary' | 'link' | 'info' | 'success' | 'warning' | 'danger'is-{color} on the wrapper
disabledbooleanfalseDisables the native select
errorbooleanfalseForce the invalid state — paints is-danger on the wrapper
error-messagesstring | string[]Manual errors, merged with rule errors
formstringId of the form to associate with. Snapshotted at setup
idstring | numberautoSelect id; generated when omitted. Snapshotted at setup
loadingbooleanfalseis-loading on the wrapper — replaces the dropdown arrow
multiplebooleanfalseis-multiple on the wrapper plus the native multiple attr
namestringForm field name. Snapshotted at setup
requiredbooleanMarks the field required. Snapshotted at setup; the DOM attr stays reactive
roundedbooleanfalseis-rounded on the wrapper
rowsnumber | stringNative size attribute — visible option rows
rulesValidationRule[][]Validation rules. Snapshotted at setup
size'small' | 'normal' | 'medium' | 'large'is-{size} on the wrapper — omitted until passed
validate-onValidateOn'blur'When validation runs

The default slot is the native <select> content. Slot props: errors, isFocused, isValid.

id, name, form, required and rules are snapshotted at setup because createInput takes plain values for those options.

Accessibility

The widget is a native <select>. The UA provides the accessible name (a sibling BuLabel with for, or a wrapping label), the list of options, and the keyboard map. BuSelect adds aria-invalid when isValid === false.

Naming

Point a BuLabel at the select’s id, or wrap the field the way Bulma does. The wrapper div.select is presentational; it does not name the control.

Keyboard

Arrow keys, Home / End, typeahead and Space are native. There is no roving tabindex and no aria-activedescendant. Do not reach for v0 Select to “upgrade” this — you would lose the markup your CSS is written for.

Validation

aria-invalid is the only extra state. There is no is-danger on the native element; the class lands on .select. A sibling BuHelp validation still needs an ambient Input.Root to wire aria-errormessagecreateInput on BuSelect is not that context.

Was this page helpful?

© 2016-1970 Vuetify, LLC
Services
Ctrl+/