Skip to content

BbTextarea

The multi-line text field: fixed rows, auto-growing height and inline character counters.

import { BbTextarea } from 'bitboss-ui';

On this page

Use it for

Reach for BbTextarea when the text runs to more than one line: a comment, a bio, release notes, a support message, a postal address.

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.

Ask whether someone will ever want a second line, not whether the value happens to be long today.

BbTextarea is BbTextInput with a taller box, and it shares the whole input chrome. That vocabulary is documented once, on BbTextInput. This page covers what is different: height, the Enter constraint, and counters.

Default

A label and a v-model, exactly as on a text input.

Same model contract too: string | null, and null once the field is emptied, never ''. label is display and accessibility only, so pass name whenever the value is submitted natively or read from FormData, and id to keep prerendered markup stable.

There is no type, no mask and no input-mode here. Multi-line text is text.

Rows and auto-grow

This is the one decision the component adds, and it has three answers.

rows on its own fixes the visible height and leaves the native resize handle in place: content scrolls, and the user can drag the box taller. Choose it when a stable form layout matters more than seeing every line at once.

auto-grow on its own drops the handle and grows the field to fit its content, animated through BbSmoothHeight. Choose it when people should see everything they wrote: a composer, a review, a message.

Together, rows becomes a floor. The field starts at that height, grows past it as the content demands, and never shrinks below it. Use it instead of a CSS min-height, which fights the growth calculation.

The trade-off: auto-grow moves everything below the field while someone types. In a long form that is a page that will not sit still, so keep it for the field people actually write in.

The shared field chrome

Everything that surrounds the box is the input family's, unchanged.

Two or three sentences is plenty.
Keep the bio under 160 characters.
No postal code yet — couriers may reject this address.

description sits above the field and is read before typing. hint appears below it on focus, and persistent-hint keeps it there. errors and warnings are the red and the amber channel, both wired to the <textarea> through aria-describedby. Errors and warnings has the full account.

clearable, loading, disabled and readonly behave as they do on a text input, down to loading being a status light rather than a lock. See Clearable, loading, disabled and readonly. readonly is the more useful of the last two here: a long block of terms or generated text is something people need to select and copy.

label-mode works too. floating and inside place the label in the field, which on a tall box is a stronger effect than on a single-line one. Pick the mode for the product rather than the field, through defaultInputLabelMode in the plugin config.

Enter does not reach your form

In a textarea Enter means "new line", and the component makes sure of it. It calls stopPropagation() on keydown, so no ancestor ever sees the key.

Amara Okonkwo · 2 days ago

Can we ship this behind the beta-editor flag first?

Lukas Brandt · yesterday

Flag added — ready for another look.

That is a wider consequence than it looks. A ⌘+Enter send shortcut bound on the form, a list with arrow-key navigation, a wrapper that closes on Escape: none of them fire while focus is in this field, and nothing warns you. Wire the action to a button instead, which is the better interaction anyway.

input is stopped the same way, so an ancestor listening for input events across a whole form will not hear this field either. The component's own keydown and input events still fire. Bind to those, or watch the model.

Because the button owns the action, an async click handler gets BbButton's loading state for free while the comment saves.

Character counters

A counter belongs in the suffix slot, beside the value rather than below it, where it would compete with the hint.

0/280The more detail you give, the faster we can help.

A counter is visual only. The textarea enforces nothing, and someone using a screen reader does not see a number ticking down. When the cap is a hard rule, surface it through errors as well, as the demo does.

Do not reach for a maxlength attribute to enforce it. It is not a prop of this component, so it lands on the outer container and constrains nothing. Truncating what someone typed in silence is worse than telling them anyway.

The rest of the affix ring is the same as on a text input, including the append position's priority order. Icons and affixes covers it.

Density and layout

compact reduces the control height, per view rather than per field.

direction puts the label beside the box instead of above it: direction="horizontal" for a 50/50 split, two space-separated tokens for a ratio, reverse to swap the columns. A textarea is a good candidate. The label beside the field keeps a settings page scannable when one row is four lines tall.

As everywhere in the family, direction applies only while the resolved label mode is outside. floating and inside embed the label in the field and force the vertical layout, so direction is ignored without a warning. Check defaultInputLabelMode before assuming the prop is broken.