Skip to content
Components, switch product

Textarea

<gui-textarea> is a multi-line text field. It renders a native <textarea> with its label, hint and errors, and can count the characters against a limit.

Code of the example above
TextareaBasic.tsx
import { GuiTextarea } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextareaBasic() {
return <GuiTextarea label="Message" />;
}

The element’s entry point is @golemui/gui-components/textarea, which registers <gui-textarea> and nothing else. To install the package and load the stylesheet, see Installation.

The @golemui/gui-components/react components have typed props and an on* prop for each event. React 18 or later.

MessageField.tsx
import { useState } from 'react';
import { GuiTextarea } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function MessageField() {
const [message, setMessage] = useState('');
return (
<GuiTextarea
label="Message"
name="message"
value={message}
onGuiChange={(event) => setMessage(event.detail.value ?? '')}
/>
);
}

Properties

Property Type Default Description
localeId attribute locale-id string 'en' BCP 47 locale for formatting and parsing, such as en-US or es.
value string – The text.
placeholder string – Text shown while the control is empty.
autocomplete string – The autocomplete hint passed to the inner native control.
counterMode attribute counter-mode 'remaining' | 'current' – With maxLength, whether the counter shows the characters left (remaining) or used (current).
minimumHeight attribute minimum-height number – Minimum height of the field, in pixels.
autoGrow attribute auto-grow boolean false Grows the field with its content instead of scrolling.
maxLength attribute maxlength number – The number of characters the counter counts against. It only drives the counter: longer text is not blocked, and the counter marks it as over the limit.
uid string – The id of the element's control. The ids of its label, hint and errors derive from it. Optional: when it is not set, the element generates one from its tag name and a page-wide counter, e.g. gui-textinput-3. Generated ids depend on creation order, so a server-rendered element must set uid or its ids will not match after hydration.

Form control properties

Every field has these, from the base class of the form controls.

Property Type Default Description
name string – The name the value is submitted under. Without it the element stays out of its form. Reflected, like a native control's: the form reads a single value's name from the attribute.
label string – The visible label, which also names the control for assistive technology. Without one the control has no accessible name; the date, time, range, tags and file fields warn about it in development builds.
hint string – Help text shown under the label and announced as the control's description.
errors string[] [] Error messages to show, typically from your own validation. They show as soon as they are set, unless touched is false.
touched boolean – Whether the user has interacted with the control. Leave it unset unless you validate on interaction: false holds errors back until it becomes true.
required boolean false The control needs a value: it is marked as required and, with a name, blocks submission.
readOnly attribute readonly boolean – The value can be read and focused but not changed.
disabled boolean – Disabled by its own attribute or by a disabled <fieldset> around it. Setting the property sets the attribute, like a native control's.
form read-only, no attribute HTMLFormElement | null – The <form> the element belongs to, if any.
validity read-only, no attribute ValidityState – The element's validity, like a native control's.
validationMessage read-only, no attribute string – The message the browser shows for an invalid value.

Methods

Method Description
checkValidity(): boolean –
reportValidity(): boolean –

Events

Every event bubbles and is composed, so you can listen on any ancestor.

Event Description
gui-input The user changed the value. detail.value is the new value.
gui-change The user committed the text, on blur. detail.value is the value.
gui-blur Focus left the control.

maxlength shows a counter under the field. By default it counts the characters left; with counter-mode="current" it counts the characters used. The limit only drives the counter: the user can type past it, and the counter turns to the error color and goes below zero.

Code of the example above
TextareaCounter.tsx
import { GuiTextarea } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextareaCounter() {
return (
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell">
<GuiTextarea
label="Status"
maxLength={140}
value="Packing for Lisbon."
/>
</div>
<div className="gui-grid__cell">
<GuiTextarea
label="Bio"
maxLength={160}
counterMode="current"
value="Mathematician. Writes notes longer than the paper they annotate."
/>
</div>
</div>
);
}

To stop a longer text, check its length in your own validation and set errors.

The field starts 120 pixels tall; minimum-height changes that, in pixels. The user can drag its corner to make it taller. With auto-grow, the field grows with its text instead of scrolling, and never gets shorter than minimum-height. Type a few lines in both fields to compare.

Code of the example above
TextareaHeight.tsx
import { GuiTextarea } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextareaHeight() {
return (
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell">
<GuiTextarea label="Short note" minimumHeight={60} />
</div>
<div className="gui-grid__cell">
<GuiTextarea
label="Meeting notes"
minimumHeight={60}
autoGrow
hint="Grows as you type."
/>
</div>
</div>
);
}

A disabled field can’t be focused or changed, and a form doesn’t submit it. A readonly field can be focused, selected and copied, and a form submits its value.

Code of the example above
TextareaDisabledReadonly.tsx
import { GuiTextarea } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextareaDisabledReadonly() {
return (
<div className="gui-grid gui-grid--row">
<div className="gui-grid__cell">
<GuiTextarea
label="Delivery notes"
value="Leave it with the concierge."
disabled
/>
</div>
<div className="gui-grid__cell">
<GuiTextarea
label="Terms"
value="Plans renew every month. Cancel any time from Settings."
readOnly
/>
</div>
</div>
);
}

placeholder, hint and errors work as on the text input.

With a name, <gui-textarea> submits its text, line breaks included, under that name, and required blocks an empty submit. maxlength doesn’t block a submit.

Code of the example above
TextareaNativeForm.tsx
import { useState, type SubmitEvent } from 'react';
import { GuiButton, GuiTextarea } from '@golemui/gui-components/react';
import '@golemui/gui-components/index.css';
export function TextareaNativeForm() {
const [result, setResult] = useState('');
const onSubmit = (event: SubmitEvent<HTMLFormElement>) => {
event.preventDefault();
const data = Object.fromEntries(new FormData(event.currentTarget));
setResult(`Submitted ${JSON.stringify(data)}`);
};
return (
<form id="feedback" onSubmit={onSubmit}>
<GuiTextarea
label="What could we do better?"
name="feedback"
maxLength={500}
required
/>
<GuiButton type="submit" label="Send feedback" />
<p id="feedback-result" role="status">
{result}
</p>
</form>
);
}

How the elements submit, validate and reset is in Native forms.

<gui-textarea> renders into the light DOM, so you can style every part with plain CSS. These are the parts and their public classes:

gui-textarea
<gui-textarea class="gui-field">
<label class="gui-label" for="bio" id="bio_label">
<span class="gui-label__text">Bio</span>
<div class="gui-widget-hint" id="bio_hint" aria-hidden="true">
Shown on your profile.
</div>
</label>
<div class="gui-widget">
<textarea
id="bio"
class="gui-widget-input"
style="height: 120px; min-height: 120px;"
aria-describedby="bio_hint"
></textarea>
</div>
<div class="gui-textarea--validation">
<div>
<div
class="gui-validator gui-validator--empty"
id="bio_errors"
role="alert"
></div>
</div>
<div class="gui-textarea--counter">
<span>96</span>
<span> / 160</span>
</div>
</div>
</gui-textarea>
ClassPart
gui-fieldThe element itself. Lines its parts up with the fields next to it in a gui-grid row.
gui-labelThe label, with the text and the hint.
gui-label__textThe label text and the required marker.
gui-widget-hintThe hint.
gui-widgetThe box around the control.
gui-widget-inputThe <textarea>, the part that looks like a field.
gui-textarea--validationThe row under the field, with the errors and the counter.
gui-validatorThe errors container. It is always there, so the alert role announces new errors.
gui-validator--emptyOn the errors container while there are no errors.
gui-validator__errorOne error message.
gui-textarea--counterThe counter, with maxlength only.
gui-textarea--counter__errorOn the counter while the text is over the limit.

The element sets the field’s height in an inline style, from minimum-height and, with auto-grow, from the text. The ids derive from uid, or from a generated id such as gui-textarea-3 when you don’t set one. Select by class or by tag, not by id.

The field reads these design tokens. Set them on :root to change every field, or on any element to change the fields inside it:

TokenWhat it styles
--gui-radius-mdThe corner radius.
--gui-bg-surfaceThe field’s background.
--gui-border-defaultThe border.
--gui-text-defaultThe text and the label.
--gui-font-familyThe font.
--gui-font-smThe text size of the label, the field and the counter.
--gui-font-xsThe text size of the hint and the errors.
--gui-text-secondaryThe hint.
--gui-space-2The field’s top and bottom padding, and the space over the counter.
--gui-space-3The field’s side padding.
--gui-border-focusThe border while focused.
--gui-shadow-focusThe focus ring.
--gui-intent-errorThe border, the messages and the counter over the limit.
--gui-shadow-focus-errorThe focus ring when there are errors.
--gui-bg-disabledThe background when disabled.
--gui-text-disabledThe text when disabled.
--gui-space-1The space between the label, hint and errors.

To restyle beyond the tokens, see Overriding styles; for dark mode and the Clay theme, see Themes and dark mode.

  • Name. The label names the field. Without one the field has no accessible name.
  • Description. The hint is the field’s description, through aria-describedby.
  • Errors. While errors show, the textarea has aria-invalid="true" and points at them with aria-errormessage, and screen readers announce new errors as they appear.
  • Required. required sets aria-required; the visual marker is hidden from screen readers.
  • Counter. The counter is visual only: it isn’t linked to the field and screen readers don’t announce it. If the limit matters, say it in the hint.
KeyAction
TabMoves focus to the field, and away from it.
EnterAdds a new line. Leaving the field commits the value and fires gui-change.
Every editing keyWorks as in any native textarea.

What every element shares, such as translating the browser messages, is in Accessibility.