Textarea
Multi-line text input — sizes, resize control, auto-grow, and a character counter, in React (@bpdm/ui) and Angular (@bpdm/ng).
The Textarea is a multi-line text field on the same tokens as Input —
three sizes, a resize control, optional autoResize (grows with content), and a
showCount character counter. In React it's <Textarea>; in Angular it's the
bpdmTextarea directive on a native <textarea>.
Usage
Drop it in like a native <textarea> — it's full-width, themed, and accessible by default.
import { Textarea } from '@bpdm/ui/textarea';
export function Comment() {
return <Textarea placeholder="Write a comment…" />;
}import { Component } from '@angular/core';
import { BpdmTextarea } from '@bpdm/ng';
@Component({
selector: 'comment',
imports: [BpdmTextarea],
template: `<textarea bpdmTextarea placeholder="Write a comment…"></textarea>`,
})
export class Comment {}Sizes
size sets the padding and text scale — sm, md (default), or lg.
import { Textarea } from '@bpdm/ui/textarea';
export function Sizes() {
return (
<div className="flex flex-col gap-3">
<Textarea size="sm" placeholder="Small" />
<Textarea size="md" placeholder="Medium (default)" />
<Textarea size="lg" placeholder="Large" />
</div>
);
}import { Component } from '@angular/core';
import { BpdmTextarea } from '@bpdm/ng';
@Component({
selector: 'sizes',
imports: [BpdmTextarea],
template: `
<div class="flex flex-col gap-3">
<textarea bpdmTextarea size="sm" placeholder="Small"></textarea>
<textarea bpdmTextarea size="md" placeholder="Medium (default)"></textarea>
<textarea bpdmTextarea size="lg" placeholder="Large"></textarea>
</div>
`,
})
export class Sizes {}Resize
resize controls the drag handle — vertical (default), none, or both.
import { Textarea } from '@bpdm/ui/textarea';
export function Resize() {
return (
<div className="flex flex-col gap-3">
<Textarea resize="none" placeholder="This one can't be resized." />
<Textarea resize="both" placeholder="Drag the corner to resize." />
</div>
);
}import { Component } from '@angular/core';
import { BpdmTextarea } from '@bpdm/ng';
@Component({
selector: 'resize',
imports: [BpdmTextarea],
template: `
<div class="flex flex-col gap-3">
<textarea bpdmTextarea resize="none" placeholder="This one can't be resized."></textarea>
<textarea bpdmTextarea resize="both" placeholder="Drag the corner to resize."></textarea>
</div>
`,
})
export class Resize {}Auto-resize
Set autoResize and the field grows to fit its content — no scrollbar, and manual
resize is turned off.
import { Textarea } from '@bpdm/ui/textarea';
export function AutoResize() {
return <Textarea autoResize placeholder="Type — I grow to fit." />;
}import { Component } from '@angular/core';
import { BpdmTextarea } from '@bpdm/ng';
@Component({
selector: 'auto-resize',
imports: [BpdmTextarea],
template: `<textarea bpdmTextarea autoResize placeholder="Type — I grow to fit."></textarea>`,
})
export class AutoResize {}Character count
Pass showCount to show a live counter, paired with maxLength (React) /
maxlength (Angular) for an n / max readout.
import { Textarea } from '@bpdm/ui/textarea';
export function Count() {
return <Textarea showCount maxLength={120} placeholder="Up to 120 characters…" />;
}import { Component } from '@angular/core';
import { BpdmTextarea } from '@bpdm/ng';
@Component({
selector: 'count',
imports: [BpdmTextarea],
template: `
<textarea bpdmTextarea showCount maxlength="120" placeholder="Up to 120 characters…"></textarea>
`,
})
export class Count {}States
Disabled dims and blocks input; aria-invalid turns the border destructive-red.
import { Textarea } from '@bpdm/ui/textarea';
export function States() {
return (
<div className="flex flex-col gap-3">
<Textarea disabled defaultValue="You can't edit this field." />
<Textarea aria-invalid defaultValue="This value needs attention." />
</div>
);
}import { Component } from '@angular/core';
import { BpdmTextarea } from '@bpdm/ng';
@Component({
selector: 'states',
imports: [BpdmTextarea],
template: `
<div class="flex flex-col gap-3">
<textarea bpdmTextarea disabled value="You can't edit this field."></textarea>
<textarea bpdmTextarea aria-invalid="true" value="This value needs attention."></textarea>
</div>
`,
})
export class States {}API
Textarea / textarea[bpdmTextarea]
Extends the native <textarea> — placeholder, value, defaultValue, rows,
maxLength, disabled, onChange, etc. all work as usual.
| Prop | Type | Default | Description |
|---|---|---|---|
size | sm | md | lg | md | Padding + text scale. |
resize | none | vertical | both | vertical | Drag-handle behaviour. |
autoResize | boolean | false | Grow to fit content (disables manual resize). |
showCount | boolean | false | Show a character counter (pairs with maxLength / maxlength), linked to the field via aria-describedby. |
className | string (Angular class) | — | Extra classes. |
Accessibility
- Renders a native
<textarea>, so it's focusable and labelable out of the box — associate a<label>(viahtmlFor/id) oraria-label. - Invalid state is set with
aria-invalid— it colours the border and is announced by assistive tech (never colour-only). - Disabled uses the native
disabledattribute (removed from the tab order). showCountrenders a character counter that's automatically linked to the field viaaria-describedby, so screen readers get the current count and limit as the field's description (anyaria-describedbyyou pass is preserved).
Internationalization
- The textarea renders no text of its own — pass a translated
placeholder/ accessible name; theshowCountreadout is numeric (n / max), so it's locale-neutral. - RTL-safe: alignment uses a logical property (
text-end), so it mirrors underdir="rtl". See the Internationalization guide.