Form

BootstrapVueNext form component and helper components that optionally support inline form styles and validation states. Pair them up with other BootstrapVueNext form control components for an easy customized, and responsive, layout with a consistent look and feel.

Introduction to forms and controls

Be sure to use an appropriate type on all inputs (e.g., email for email address or number for numerical information) to take advantage of newer input controls like email verification, number selection, and more.

Here is a quick example to demonstrate BootstrapVueNext's form styles. Keep reading for documentation on supported components, form layout, and more.

We'll never share your email with anyone else.
Form Data Result
{
  "email": "",
  "name": "",
  "food": null,
  "checked": []
}
HTML
vue
<template>
  <BForm
    v-if="show"
    @submit="onSubmit"
    @reset="onReset"
  >
    <BFormGroup
      id="input-group-1"
      label="Email address:"
      label-for="input-1"
      description="We'll never share your email with anyone else."
    >
      <BFormInput
        id="input-1"
        v-model="form.email"
        type="email"
        placeholder="Enter email"
        required
      />
    </BFormGroup>

    <BFormGroup
      id="input-group-2"
      label="Your Name:"
      label-for="input-2"
    >
      <BFormInput
        id="input-2"
        v-model="form.name"
        placeholder="Enter name"
        required
      />
    </BFormGroup>
    <BFormGroup
      id="input-group-3"
      label="Food:"
      label-for="input-3"
    >
      <BFormSelect
        id="input-3"
        v-model="form.food"
        :options="foods"
        required
      />
    </BFormGroup>

    <BFormGroup id="input-group-4">
      <BFormCheckboxGroup
        id="checkboxes-4"
        v-model="form.checked"
      >
        <BFormCheckbox value="me">Check me out</BFormCheckbox>
        <BFormCheckbox value="that">Check that out</BFormCheckbox>
      </BFormCheckboxGroup>
    </BFormGroup>
    <BButton
      type="submit"
      variant="primary"
      >Submit</BButton
    >
    <BButton
      type="reset"
      variant="danger"
      >Reset</BButton
    >
  </BForm>

  <BCard
    class="mt-3"
    header="Form Data Result"
  >
    <pre class="m-0">{{ form }}</pre>
  </BCard>
</template>

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

const foods = [
  {text: 'Select One', value: null as string | null},
  {text: 'Carrots', value: 'Carrots'},
  {text: 'Beans', value: 'Beans'},
  {text: 'Tomatoes', value: 'Tomatoes'},
  {text: 'Corn', value: 'Corn'},
]

const form = reactive<{
  email: string
  name: string
  food: string | null
  checked: string[]
}>({
  email: '',
  name: '',
  food: null,
  checked: [],
})
const show = ref(true)

const onSubmit = (event: Event) => {
  event.preventDefault()
  // eslint-disable-next-line no-alert
  alert(JSON.stringify(form))
}

const onReset = (event: Event) => {
  event.preventDefault()
  // Reset our form values
  form.email = ''
  form.name = ''
  form.food = null
  form.checked = []
  // Trick to reset/clear native browser form validation state
  show.value = false
  nextTick(() => {
    show.value = true
  })
}
</script>

Inline form

Bootstrap 5 has dropped form-specific layout classes for the grid system. See the Bootstrap 5 Changelog.

To create horizontal forms with the grid add the .row class to form groups and use the .col-_-_ classes to specify the width of your labels and controls. Be sure to add .col-form-label to your <label>s as well, so they’re vertically centered with their associated form controls.

You may need to manually address the width and alignment of individual form controls with spacing utilities (as shown below). Lastly, be sure to always include a <label> with each form control, even if you need to hide it from non-screenreader visitors with class .visually-hidden.

@
HTML
template
<BForm class="d-flex flex-row align-items-center flex-wrap">
  <label
    class="col-form-label visually-hidden"
    for="inline-form-input-name"
    >Name</label
  >
  <div class="col-lg-3 me-2 my-2">
    <BFormInput
      id="inline-form-input-name"
      placeholder="Jane Doe"
    />
  </div>
  <label
    class="col-form-label visually-hidden"
    for="inline-form-input-username"
    >Username</label
  >
  <div class="col-lg-3 me-2 my-2">
    <BInputGroup
      prepend="@"
      class="col-lg-4"
    >
      <BFormInput
        id="inline-form-input-username"
        placeholder="Username"
      />
    </BInputGroup>
  </div>
  <div class="col-lg-3 me-2 my-2">
    <BFormCheckbox>Remember me</BFormCheckbox>
  </div>
  <div class="col-lg-1 my-2">
    <BButton variant="primary">Save</BButton>
  </div>
</BForm>

Custom form controls and selects are also supported.

HTML
vue
<template>
  <BForm>
    <div class="row">
      <label
        class="col-form-label col-lg-2 me-sm-2"
        for="inline-form-custom-select-pref"
        >Preference</label
      >
      <div class="col-lg-2">
        <BFormSelect
          id="inline-form-custom-select-pref"
          v-model="customSelect"
          class="mb-2 me-sm-2 mb-sm-0"
          :options="options"
        />
      </div>
      <BFormCheckbox class="col-form-label col-lg-3 mb-2 me-sm-2 mb-sm-0"
        >Remember my preference</BFormCheckbox
      >
      <div class="col-lg-2 col-form-label">
        <BButton variant="primary">Save</BButton>
      </div>
    </div>
  </BForm>
</template>

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

const options = [
  {text: 'Choose...', value: null as string | null},
  {text: 'One', value: 'One'},
  {text: 'Two', value: 'Two'},
  {text: 'Three', value: 'Three'},
]

const customSelect = ref<string | null>(null)
</script>

Floating Labels

Wrap a BFormInput, BFormTextarea, or BFormSelect in a BFormFloatingLabel to enable floating labels. A placeholder is required on each input in order to make the Bootstrap 5 css work correctly.

HTML
template
<BForm>
  <BFormFloatingLabel
    label="Email address"
    label-for="floatingEmail"
    class="my-2"
  >
    <BFormInput
      id="floatingEmail"
      type="email"
      placeholder="Email address"
    />
  </BFormFloatingLabel>
  <BFormFloatingLabel
    label="Password"
    label-for="floatingPassword"
    class="my-2"
  >
    <BFormInput
      id="floatingPassword"
      type="password"
      placeholder="Password"
    />
  </BFormFloatingLabel>
</BForm>

Floating labels work correctly for disabled state and readonly states. In addition to styled textual inputs, floating labels also work for plaintext inputs, textareas, input groups and selects. See the Bootstrap 5 documentation for more details.

The floating attribute on the BForm component only applies to single form controls like this:

HTML
template
<BForm floating>
  <BFormInput
    id="floatingFormInputValue"
    type="email"
    placeholder="name@example.com"
  />
  <label for="floatingFormInputValue">Input with value</label>
</BForm>

Accessibility

Ensure that all form controls have an appropriate accessible name so that their purpose can be conveyed to users of assistive technologies. The simplest way to achieve this is to use a <label> element, or—in the case of buttons—to include sufficiently descriptive text as part of the <button>...</button> content.

For situations where it’s not possible to include a visible <label> or appropriate text content, there are alternative ways of still providing an accessible name, such as:

  • <label> elements hidden using the .visually-hidden class
  • Pointing to an existing element that can act as a label using aria-labelledby
  • Providing a title attribute
  • Explicitly setting the accessible name on an element using aria-label

If none of these are present, assistive technologies may resort to using the placeholder attribute as a fallback for the accessible name on <input> and <textarea> elements. The examples in this section provide a few suggested, case-specific approaches.

While using visually hidden content (.visually-hidden, aria-label, and even placeholder content, which disappears once a form field has content) will benefit assistive technology users, a lack of visible label text may still be problematic for certain users. Some form of visible label is generally the best approach, both for accessibility and usability.

See also:

Form helper components

The following helper components are available with the Form plugin:

  • BFormText Help text blocks for inputs
  • BFormInvalidFeedback Invalid feedback text blocks for input invalid states
  • BFormValidFeedback Valid feedback text blocks for input valid states
  • BFormDatalist Easily create a <datalist> for use with BFormInput or plain <input>

Form text helper

Display a block of help text below an input with the BFormText helper component. text is displayed with a muted color and slightly smaller font-size.

Tip

Help text should be explicitly associated with the form control it relates to using the aria-describedby attribute. This will ensure that assistive technologies, such as screen readers, will announce this help text when the user focuses or enters the control.

Your password must be 8-20 characters long, contain letters and numbers, and must not contain spaces, special characters, or emoji.
HTML
template
<BForm @submit.stop.prevent>
  <label for="text-password">Password</label>
  <BFormInput
    id="text-password"
    type="password"
    aria-describedby="password-help-block"
  />
  <BFormText id="password-help-block">
    Your password must be 8-20 characters long, contain letters and numbers, and must not contain
    spaces, special characters, or emoji.
  </BFormText>
</BForm>

Feedback helpers

The BFormValidFeedback and BFormInvalidFeedback helper components will display feedback (based on input state) as a block of colored text. They rely on being placed after an input (sibling) and will show based on the browser native validation state of the input. To force them to show, set the prop force-show to true, or bind the controls state to the state prop of the feedback helper, or set the was-validated class on a parent element (such as a form). See the Validation section below for additional details.

Use the optional Boolean prop tooltip to change the display from a block to a static tooltip style. The feedback will typically appear below the form control. When this mode is enabled, it is important that the parent container have a position: relative: css style (or position-relative class). Note that tooltip style feedback may, since its positioning is static, obscure other inputs, labels, etc.

NOTE

Some form controls, such as BFormRadio and BFormCheckbox have wrapper elements which will prevent the feedback text from automatically showing (as the feedback component is not a direct sibling of the form control's input). Use the feedback component's state prop (bound to the state of the form control) or the force-show prop to display the feedback.

Your user Id must be 5-12 characters long.
Looks Good.
HTML
vue
<template>
  <BForm @submit.stop.prevent>
    <label for="feedback-user">User Id</label>
    <BFormInput
      id="feedback-user"
      v-model="userId"
      :state="validation"
    />
    <BFormInvalidFeedback :state="validation">
      Your user Id must be 5-12 characters long.
    </BFormInvalidFeedback>
    <BFormValidFeedback :state="validation"> Looks Good. </BFormValidFeedback>
  </BForm>
</template>

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

const userId = ref('')

const validation = computed(() => userId.value.length > 4 && userId.value.length < 13)
</script>

Datalist helper

For browsers that support <datalist> elements, the <BFormDatalist> helper component will allow you to quickly create a <datalist> and child <option> elements via an array passed to the options prop.

You may also manually provide <option> elements inside <BFormDatalist>. They will appear below any <option> elements generated from the options prop. Or use the first slot to place options above the generated options.

HTML
vue
<template>
  <div>
    <label for="input-with-list">Input with datalist</label>
    <BFormInput
      id="input-with-list"
      list="input-list"
    />
    <BFormDatalist
      id="input-list"
      :options="datalistOptions"
    />
  </div>
</template>

<script setup lang="ts">
const datalistOptions = ['Apple', 'Banana', 'Grape', 'Kiwi', 'Orange']
</script>

See also:

  • <BFormInput> datalist for datalist usage.
  • <BFormSelect> options prop docs for details on the formats and helper props associated with options. Note that <BFormDatalist> only support a flat list of BFormSelectOptions, unlike <BFormSelect> which support a heirarchy of BFormSelectOption and BFormSelectOptionGroup.

TypeScript Type Safety

BFormDatalist provides full TypeScript type safety through generic type parameters. When you provide typed options, TypeScript will:

  1. Validate field names - Ensure value-field, text-field, and disabled-field props reference actual keys of your option type
  2. Infer v-model type - Automatically determine the correct type for v-model based on your value-field

Basic type-safe usage:

Selected user ID:
HTML
vue
<template>
  <!-- #region template -->
  <div>
    <BFormInput
      v-model="selectedUserId"
      type="text"
      list="user-list"
      placeholder="Type to search users..."
    />
    <BFormDatalist
      id="user-list"
      :options="users"
      value-field="id"
      text-field="name"
      disabled-field="inactive"
    />
    <div class="mt-3">
      Selected user ID: <strong>{{ selectedUserId }}</strong>
    </div>
  </div>
  <!-- #endregion template -->
</template>

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

interface User {
  id: number
  name: string
  inactive?: boolean
}

const users: User[] = [
  {id: 1, name: 'Alice Smith'},
  {id: 2, name: 'Bob Johnson'},
  {id: 3, name: 'Charlie Brown', inactive: true},
  {id: 4, name: 'David Wilson'},
]

// TypeScript knows selectedUserId is a number (matching the id field type)
const selectedUserId = ref<number>()
</script>

Type-safe API responses:

Selected:
HTML
vue
<template>
  <!-- #region template -->
  <div>
    <BFormInput
      v-model="selectedProductCode"
      type="text"
      list="product-list"
      placeholder="Enter product code..."
    />
    <BFormDatalist
      id="product-list"
      :options="apiProducts"
      value-field="productCode"
      text-field="productName"
      disabled-field="discontinued"
    />
    <div class="mt-3">
      Selected: <strong>{{ selectedProductCode }}</strong>
    </div>
  </div>
  <!-- #endregion template -->
</template>

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

interface ApiProduct {
  productCode: string
  productName: string
  category: string
  discontinued?: boolean
}

// Simulated API response
const apiProducts: ApiProduct[] = [
  {productCode: 'PROD-001', productName: 'Widget A', category: 'Hardware'},
  {productCode: 'PROD-002', productName: 'Gadget B', category: 'Electronics'},
  {productCode: 'PROD-003', productName: 'Tool C', category: 'Hardware', discontinued: true},
]

// TypeScript knows selectedProductCode is a string (matching productCode field type)
const selectedProductCode = ref<string>()
</script>

Type-safe enums:

Selected: us
HTML
vue
<template>
  <!-- #region template -->
  <div>
    <BFormInput
      v-model="selectedCountry"
      type="text"
      list="country-list"
      placeholder="Select country..."
    />
    <BFormDatalist
      id="country-list"
      :options="countryOptions"
      value-field="code"
      text-field="name"
    />
    <div class="mt-3">
      Selected: <strong>{{ selectedCountry }}</strong>
    </div>
  </div>
  <!-- #endregion template -->
</template>

<script setup lang="ts">
import {ref} from 'vue'
import {Country} from './DatalistTypeSafeEnumTypes'

interface CountryOption {
  code: Country
  name: string
}

const countryOptions: CountryOption[] = [
  {code: Country.US, name: 'United States'},
  {code: Country.UK, name: 'United Kingdom'},
  {code: Country.CA, name: 'Canada'},
  {code: Country.AU, name: 'Australia'},
]

// TypeScript knows selectedCountry is a Country enum value
const selectedCountry = ref<Country>(Country.US)
</script>

Benefits:

  • IDE autocomplete - Your editor suggests valid field names as you type
  • Compile-time validation - Typos and invalid field names are caught before runtime
  • Type inference - The v-model type is automatically inferred from your value field
  • Refactoring safety - Renaming fields in your interface updates all usages

Backward compatibility:

Type safety is completely opt-in and maintains 100% backward compatibility. Existing code without explicit types continues to work exactly as before:

vue
<!-- Works without type annotations -->
<ComponentName v-model="selected" :options="items" />

To enable type safety, simply provide explicit types for your data:

typescript
interface MyItem {
  id: number
  name: string
}

const items: MyItem[] = [...]

See the BFormSelect TypeScript documentation for comprehensive type safety guidance that also applies to datalists.

Global Defaults Limitation

Due to technical limitations with TypeScript generic components, BFormDatalist cannot participate in the global defaults system provided by createBootstrap({ defaults: {...} }). Since BFormDatalist has minimal styling props (only id), this limitation has no practical impact. All props must be specified directly on the component or use their hardcoded defaults.

Validation

Disable browser native HTML5 validation by setting the novalidate prop to true on BForm.

Set the validated prop, on BForm, to true to add the Bootstrap v5 .was-validated class to the form to trigger validation states.

All form controls support a state prop, which can be used to set the form control into one of three contextual states:

  • false (denotes invalid state) is great for when there is a blocking or required field. A user must fill in this field properly to submit the form
  • true (denotes valid state) is ideal for situations when you have per-field validation throughout a form and want to encourage a user through the rest of the fields
  • null Displays no validation state (neither valid nor invalid)

Refer to the Bootstrap v5 Form Validation Documentation for details on the Bootstrap v5 validation states.

Component Reference

PropTypeDefaultDescription
floatingbooleanfalse When set, renders a single control form with a floating label. This only works for forms where the immediate children are a label and one of the supported controls. See above for details.
idstringundefined Used to set the `id` attribute on the rendered content, and used as the base to generate any additional element IDs as needed
novalidatebooleanfalse When set, disables browser native HTML5 validation on controls in the form
validatedbooleanfalse When set, adds the Bootstrap class 'was-validated' on the form, triggering the native browser validation states
NameScopeDescription
defaultContent to place in the form

<BFormDatalist>

PropTypeDefaultDescription
disabled-fieldstring'disabled' Field name in the `options` array that should be used for the disabled state
idstringundefined Used to set the `id` attribute on the rendered content, and used as the base to generate any additional element IDs as needed
optionsreadonly (unknown | Record<string, unknown>)[]'() => []' Array of items to render in the component. Note that BFormDatalist only supports Options, not OptionsGroups
text-fieldstring'text' Field name in the `options` array that should be used for the text label
value-fieldstring'value' Field name in the `options` array that should be used for the value
NameScopeDescription
defaultContent to place in the form datalist
firstSlot to place options above options provided via the 'options' prop
option
value: any (T) - The value of the option
text: string - The text of the option
disabled: boolean - Is the option disabled
Use this slot to have finer control over the content rendered inside each data item

<BFormFloatingLabel>

PropTypeDefaultDescription
labelstringundefined The text of the floating label
label-forstringundefined The id of the input control that the floating label is for
NameScopeDescription
defaultThe input control that contains the floating label
labelThe content to display in the floating label

<BFormInvalidFeedback>

PropTypeDefaultDescription
aria-livestringundefined When the rendered element is an `aria-live` region (for screen reader users), set to either 'polite' or 'assertive'
force-showbooleanfalse Shows the feedback text, regardless of the value of the 'state' prop
idstringundefined Used to set the `id` attribute on the rendered content, and used as the base to generate any additional element IDs as needed
rolestringundefined Sets the ARIA attribute `role` to a specific value
stateValidationStateundefined Controls the validation state appearance of the component. `true` for valid, `false` for invalid, or `null` for no validation state
tagstring'div' Specify the HTML tag to render instead of the default tag
textstringundefined The feedback text to display
tooltipbooleanfalse Renders the feedback text in a rudimentary tooltip style
NameScopeDescription
defaultContent to place in the form invalid feedback

<BFormRow>

PropTypeDefaultDescription
tagstring'div' Specify the HTML tag to render instead of the default tag
NameScopeDescription
defaultContent to place in the form row

<BFormText>

PropTypeDefaultDescription
idstringundefined Used to set the `id` attribute on the rendered content, and used as the base to generate any additional element IDs as needed
inlinebooleanfalse When set, renders the help text as an inline element, rather than a block element
tagstring'div' Specify the HTML tag to render instead of the default tag
textstringundefined The text to display
text-variantTextColorVariant | nullnull Applies one of the Bootstrap theme color variants to the text
NameScopeDescription
defaultContent to place in the form text

<BFormValidFeedback>

PropTypeDefaultDescription
aria-livestringundefined When the rendered element is an `aria-live` region (for screen reader users), set to either 'polite' or 'assertive'
force-showbooleanfalse Shows the feedback text, regardless of the value of the 'state' prop
idstringundefined Used to set the `id` attribute on the rendered content, and used as the base to generate any additional element IDs as needed
rolestringundefined Sets the ARIA attribute `role` to a specific value
stateValidationStateundefined Controls the validation state appearance of the component. `true` for valid, `false` for invalid, or `null` for no validation state
tagstring'div' Specify the HTML tag to render instead of the default tag
textstringundefined The feedback text to display
tooltipbooleanfalse Renders the feedback text in a rudimentary tooltip style
NameScopeDescription
defaultContent to place in the form valid feedback