Skip to content

BbTextInput

The single-line text field, and the chrome every other input in the family shares.

import { BbTextInput } from 'bitboss-ui';

On this page

Use it for

Reach for BbTextInput for any short, single-line string: a name, an email, a URL, a coupon code, a search box.

Use something else when

Pass Through

Hover or tap a part to outline it. Toggles flip loading, errors and warnings when the component has them — only parts highlight.

$USDGuidance while the control is focused.

This page also defines the shared field chrome used by the other inputs.

Default

Pass a label and bind v-model.

Model: "Vantera"

The model is string | null. An emptied field emits null, never '', so write value ?? '' for an API that insists on a string.

label does not set name. Pass name for native form submission and an explicit id for stable prerendered markup.

Coming from v2template ref removed

v3 removed the imperative handle. A leftover inputRef.value?.focus() is undefined at runtime, not a compile error. Use autofocus for focus on mount, and reach the native <input> by its id for anything else.

Label modes

label-mode puts the label above the field (outside, the default), resting inside it like a placeholder (floating), or pinned small inside the top edge (inside).

Set the product default with defaultInputLabelMode. Do not combine floating with a placeholder; both occupy the same space.

One spelling is worth committing to memory. floating is not a prop and never was, neither in v3 nor in v2. Written bare it lands on the root element through $attrs and does nothing at all.

diff
- <BbTextInput v-model="email" label="Email" floating />
+ <BbTextInput v-model="email" label="Email" label-mode="floating" />

hide-label drops the label from view and keeps the accessible name, which is what a search box with nothing but a placeholder wants. Dropping label itself is never the answer.

Type and keyboard

type tells the browser what kind of text this is: text, email, url, tel, search, password. That is the whole list.

v3 narrowed it to those six. type="number" and type="date" no longer compile, and the fix is a different component rather than a different value:

diff
- <BbTextInput v-model="qty" type="number" label="Quantity" />
+ <BbNumberInput v-model="qty" label="Quantity" />

- <BbTextInput v-model="due" type="date" label="Due date" />
+ <BbDatePickerInput v-model="due" label="Due date" />

Set autocomplete in auth and checkout flows. Use input-mode to request the right mobile keyboard without changing the value type.

There is no revealable prop. A reveal toggle is a type that changes plus a button in the #append slot.

Native attributes that are not component props, such as maxlength and pattern, land on the outer container and do not constrain the input.

Masks

Pass a maska config to mask and the field formats as the user types: card numbers, VAT ids, licence keys.

Card model
4242424242424242
VAT model
null

By default the model holds the unmasked value: the field shows 4242 4242 4242 4242, v-model holds 4242424242424242. Pass emit-masked when the formatted string is the value, as it is for a VAT id people copy and paste. Either way an emptied field emits null.

Masks format, they do not validate. A complete but wrong card number passes the mask and still needs an error.

Description and hint

Two channels sit around the field, and the difference is timing. description is always visible between the label and the field, read before typing. hint appears below the field on focus, read while typing.

Include the scheme — https://vantera.io

persistent-hint keeps the hint visible. Both channels are connected through aria-describedby, so keep them useful and concise.

Errors and warnings

errors says the value cannot be accepted. warnings says the value is accepted and worth a second look. Two channels, not one channel with a severity flag.

Enter a complete email address, including the domain.
Over 100 seats moves this account to annual billing.

Non-empty messages imply their visual state. Errors set aria-invalid; warnings do not. When both exist, errors win.

required forwards to the native input and produces no message of its own.

Bind server-side errors straight in. With Inertia:

vue
<BbTextInput
    v-model="form.email"
    label="Billing email"
    type="email"
    autocomplete="email"
    :errors="form.errors.email"
/>

For client rules, import the component from bitboss-ui/validated:

vue
<script setup lang="ts">
import { BbForm, BbTextInput } from 'bitboss-ui/validated';
</script>

<template>
    <BbForm @submit="save">
        <BbTextInput v-model="form.email" label="Email" rules="required|email" />
    </BbForm>
</template>

Your errors merge with rule messages. BbForm has the full setup.

Clearable, loading, disabled and readonly

Four flags change what the field lets you do, and only two of them stop anything.

Checking this code…
Chosen when the workspace was created.

clearable emits null and restores focus. loading only shows status; it does not lock the field. disabled removes interaction, while readonly keeps the value focusable and copyable.

Icons and affixes

The slots ring the field from the value outward. prefix and suffix hug the typed text, prepend and append sit inside the chrome, prepend-outer and append-outer sit beside the field entirely.

vantera.io/
EUR / month

Icons in the inner positions are decorative. Put interactive content in append-outer, outside the field chrome.

One position is contested. The append slot shows exactly one thing, by priority: clear button → spinner → error or warning icon → your append:icon. An append:icon on a field that is also clearable or validated disappears exactly when the field gets interesting. Put anything that must stay visible in append-outer.

Density and layout

compact reduces the control height. Use one density across a form.

direction puts the label beside the field instead of above it. direction="horizontal" splits the row 50/50, and two space-separated tokens read as a ratio, so "xx xxxxxx" gives a quarter to the label. reverse swaps the two columns.

direction applies only while the resolved label mode is outside, which is the library default. floating and inside put the label inside the field, force the vertical layout, and ignore direction without a warning. Check defaultInputLabelMode before concluding the prop is broken.

Coming from v2input wrapper classes

Selectors on .bb-common-input-inner-container* move to .common-input-wrapper--*. The layout class bb-base-input-container__layout--hidden-label is gone as well: it always appeared on exactly the same condition as --reverse, and nothing in the library ever painted it. Target --reverse.