Skip to content

BbTextarea

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

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

Also see: Validated forms.

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, Enter, and counters.

Passthrough

Hover or tap a part to outline it. The toggles turn on loading, errors and warnings where the component has them; only parts get a row.

Guidance while the control is focused.

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 ('' if you set the plugin option formControls.textareaNullValue to 'string'). 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 and no mask here. input-mode still reaches the <textarea> if you need a specific mobile keyboard.

Rows and auto-grow

Pick rows for a fixed box, auto-grow to fit the text, or both for a growing box with a floor.

rows on its own fixes the visible height: the box is that many lines tall and the content scrolls. There is no resize handle. 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. Empty, it is exactly one text input tall, so it sits flush in a row of text inputs. 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. With neither prop, the box is a fixed 150px.

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

Description, hint, errors and warnings work as on BbTextInput.

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, including rules, formless and validation-label. A field joined to a BbForm by its name is required through rules="required" or the schema, not the required prop, which is for a field outside a form.

clearable, loading, disabled and readonly behave as they do on a text input. readonly is the useful one here: a long block of terms or generated text is something people select and copy.

variant (including the blank none), compact and label-mode work as in Variants, density and layout. A floating label on a tall box is a stronger effect than on one line, so pick the mode for the product, through defaultInputLabelMode.

Enter is a new line

In a textarea Enter means "new line", and the component makes sure of it: a plain Enter stops at the field, so a form never submits on it.

Amara Okonkwo

Amara Okonkwo · 2 days ago

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

Lukas Brandt

Lukas Brandt · yesterday

Flag added, ready for another look.

Only a plain Enter is stopped. Every other key bubbles as usual. A ⌘/Ctrl+Enter send shortcut or a ⌘+S bound on the form fires while focus is in this field, and so does a wrapper that closes on Escape. input events bubble too. Still give the action a visible button. The component's own keydown and input events fire as well.

Character counters

Add counter and the field counts its characters at the end of the hint row: 42, or 42 / 160 when you also pass the native maxlength. That attribute reaches the <textarea>, which stops input and cuts a paste at the cap, and the counter reads the same number.

Sent the day before the appointment.Characters: 134 of 160

The count turns to body text from 90% of the limit and to the warning colour at it. A full field is not an error, so the box does not change. A screen reader hears the count as part of the field's description, and a polite live region announces it only near and at the limit, never on every keystroke.

For a soft limit, keep counter and leave maxlength off: nothing stops the typing, and the overflow goes in errors. The text stays, and the field says what to cut.

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

Density and layout

layout puts the label beside the box instead of above it: layout="horizontal" for a 50/50 split, two space-separated tokens for a ratio, reverse to swap the columns. The label beside the field keeps a panel scannable when one row is four lines tall. Use the same ratio on every row so the boxes line up. Keep it to a wide container: on a phone the box gets too narrow, so the demo passes a per-breakpoint map, { default: 'vertical', md: 'xx xxxxx' }, and stacks below md.

Release 2.4

compact tightens each row, as in the panel above. Set it per view, not per field.

As everywhere in the family, layout applies only while the resolved label mode is outside. floating and inside embed the label in the field and always stack vertically. The resolved mode is label-mode, or defaultInputLabelMode when the prop is unset.