Textarea
View as MarkdownA multi-line text input that works with Field, with an optional character counter, screen reader announcements and submit on Enter.
Features
Permalink to heading "Features"- Works standalone or directly inside
ui:field.root, nofield.controlwrapper - The
uicopy grows with its content through CSSfield-sizing: content, see the MDN docs for browser support - Optional
submitOnprop to submit the nearest form on Enter or Cmd/Ctrl+Enter - Optional
transformcallback to sanitize or reformat the value while typing, the cursor position is kept - Optional
wordCountpart with a translatable "42 / 250 characters" counter, driven bymaxLength maxLengthBehaviorlets a paste arrive whole and shows that it is overmaxLength, instead of cutting it off- Counter updates are announced to screen readers, debounced by
announceDebounce(600 ms by default). Passannounce="{false}"to turn this off
<primitives:textarea.root>
<primitives:textarea.label />
<primitives:textarea.textarea />
<primitives:textarea.wordCount />
</primitives:textarea.root>
Installation
Permalink to heading "Installation"typo3 ui:add textarea
Please copy the files manually from GitHub into your project.
Read more about installing Components and Primitives.
Examples
Permalink to heading "Examples"With field
Permalink to heading "With field"Nest ui:textarea.root directly inside ui:field.root. The textarea takes name, disabled, required, invalid and aria-describedby from the field. Label it with the primitive's own textarea.label, which targets the textarea for you. The demo is a required bio field with helper and error text.
With word count
Permalink to heading "With word count"Pass maxLength and place the wordCount part anywhere inside textarea.root. The bio in the demo counts up to 280 characters.
Submit on Enter
Permalink to heading "Submit on Enter"Set submitOn="{f:constant(name: 'Jramke\FluidPrimitives\Enum\TextareaSubmitOn::ModEnter')}" to submit the surrounding form with Cmd/Ctrl+Enter while plain Enter inserts a newline. TextareaSubmitOn::Enter submits with plain Enter and inserts a newline with Shift+Enter. Wrap the textarea in ui:form.root or a native <form>: submitOn calls its requestSubmit(). Press Cmd/Ctrl+Enter in the demo to submit it.
Allow typing past the limit
Permalink to heading "Allow typing past the limit"Set maxLengthBehavior="{f:constant(name: 'Jramke\FluidPrimitives\Enum\MaxLengthBehavior::Warn')}" to keep maxLength as a target instead of a wall. The browser cuts nothing off, the count switches to "Characters over the limit: 6" and the parts carry data-over-limit, next to data-at-limit for exactly maxLength characters and data-max-length-behavior to style warn apart from invalid. The form still submits. The demo is a product review that starts over its limit.
Enforce the limit that matters on the server. To accept a few characters over, give the template a lower maxLength than the validator, for example StringLength(maximum: 210) behind maxLength="200". A textarea submits each line break as two characters, so leave a few characters of room. The count is the one of the browser's maxlength, in UTF-16 units, so an emoji counts twice. mb_strlen and a VARCHAR(n) column count it once, so the count is never lower than the server's.
Reject text past the limit
Permalink to heading "Reject text past the limit"MaxLengthBehavior::Invalid works like Warn and also makes the surrounding field invalid with the tooLong validity, so a form does not submit until the text fits. The error shows when the user leaves the field, like any other constraint. Give ui:field.errorText a match of ValidityMatch::TooLong for your own message. Type past the limit in the demo and tab out.
With a transform callback
Permalink to heading "With a transform callback"transform runs on every input event and returns the value to write back to the textarea. A function can't be set from the template, so pass transform where you create Textarea in your own entry file. Type in lowercase and it turns uppercase as you type.
API Reference
Permalink to heading "API Reference"The following tables cover the available props of the Fluid Primitives.
textarea.root
Permalink to heading "textarea.root"Provides shared textarea state and wraps all related parts. Renders a <div> element.
| Name | Description | Required | Default |
|---|---|---|---|
placeholder | stringPlaceholder text shown when the textarea is empty. | No | - |
autocomplete | stringHints for browser autofill, e.g. street-address. See the [HTML spec](https://html.spec.whatwg.org/multipage/form-control-infrastructure.html#autofill) for the full list of tokens. | No | - |
name | stringThe name attribute of the textarea. Useful for form submission. Inherited from a surrounding field when available. | No | - |
disabled | booleanWhether the textarea is disabled. Inherited from a surrounding field when available. | No | - |
invalid | booleanWhether the textarea value is invalid. Inherited from a surrounding field when available. | No | - |
required | booleanWhether the textarea is required. Inherited from a surrounding field when available. | No | - |
readOnly | booleanWhether the textarea is readonly. Inherited from a surrounding field when available. | No | - |
defaultValue | stringThe initial value of the textarea when rendered. Use when you don't need to control the value of the textarea. Inherited from a surrounding field when available. | No | '' |
maxLength | integerThe maximum number of characters allowed, see `maxLengthBehavior` for what happens beyond it. Also drives the wordCount part, e.g. '42 / 250 characters'. | No | - |
maxLengthBehavior | Enum\MaxLengthBehaviorWhat happens when the value is longer than `maxLength`. - `block`: The browser stops the typing and cuts a paste off at the limit. - `warn`: Typing and pasting past the limit is allowed, the word count and `data-over-limit` show it, the form still submits. - `invalid`: Like `warn`, and a surrounding field also becomes invalid with the `tooLong` validity, so the form does not submit. | No | Block |
rows | integerThe number of visible text lines. | No | - |
submitOn | Enum\TextareaSubmitOnSubmits the nearest form on a keypress instead of inserting a newline: Enter submits on plain Enter (Shift+Enter still inserts a newline), ModEnter submits on Cmd/Ctrl+Enter (plain Enter always inserts a newline). Unset (the default) never intercepts Enter. | No | - |
translations | arraySpecifies the localized word count strings, `wordCount` and `wordCountOver`, with `%count%`, `%max%` and `%over%` placeholders. In a client script they are functions of `{ count, max, over }`. Use `f:translate` for per-template localization overrides when needed. | No | - |
announce | booleanWhether the word count is announced to assistive tech as the user types. Set to false to stay silent. | No | true |
announceDebounce | integerMilliseconds to debounce word count announcements by, so rapid typing doesn't spam assistive tech. Set to 0 to announce every change immediately. | No | 600 |
attributes | arrayAdditional attributes that should be rendered on the component where ui:attributes is used. | No | [] |
asChild | booleanIf true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. | No | - |
rootId | stringThe root ID of the component, used for hydration and identification. | No | - |
ids | arrayThe IDs of of the component parts for composition. | No | [] |
autoMount | booleanWhether the client initializes the component on its own. Set it to false to mount it yourself with `mount()` | No | true |
class | stringThe CSS class(es) to be applied to the component. | No | - |
Rendered data attributes
| Attribute | Description |
|---|---|
data-invalid | Present when invalid |
data-disabled | Present when disabled |
data-readonly | Present when read-only |
data-max-length-behavior | The `maxLengthBehavior` while there is a `maxLength`: `block`, `warn` or `invalid`. Style the over-limit state of `warn` and `invalid` apart. One of: "invalid", "block", "warn". |
data-at-limit | Present while the value is exactly `maxLength` characters long. |
data-over-limit | Present while the value is longer than `maxLength` and `maxLengthBehavior` is not `block`. |
textarea.label
Permalink to heading "textarea.label"Labels the textarea. Renders a <label> element.
| Name | Description | Required | Default |
|---|---|---|---|
attributes | arrayAdditional attributes that should be rendered on the component where ui:attributes is used. | No | [] |
asChild | booleanIf true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. | No | - |
class | stringThe CSS class(es) to be applied to the component. | No | - |
Rendered data attributes
| Attribute | Description |
|---|---|
data-invalid | Present when invalid |
data-disabled | Present when disabled |
data-required | Present when required |
textarea.textarea
Permalink to heading "textarea.textarea"The editable textarea. Renders a <textarea> element.
| Name | Description | Required | Default |
|---|---|---|---|
attributes | arrayAdditional attributes that should be rendered on the component where ui:attributes is used. | No | [] |
asChild | booleanIf true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. | No | - |
class | stringThe CSS class(es) to be applied to the component. | No | - |
Rendered data attributes
| Attribute | Description |
|---|---|
data-max-length-behavior | The `maxLengthBehavior` while there is a `maxLength`: `block`, `warn` or `invalid`. Style the over-limit state of `warn` and `invalid` apart. One of: "invalid", "block", "warn". |
data-at-limit | Present while the value is exactly `maxLength` characters long. |
data-over-limit | Present while the value is longer than `maxLength` and `maxLengthBehavior` is not `block`. |
data-invalid | Present when invalid |
data-disabled | Present when disabled |
data-readonly | Present when read-only |
textarea.wordCount
Permalink to heading "textarea.wordCount"Displays the character count, e.g. '42 / 250 characters'. Renders a <span> element.
| Name | Description | Required | Default |
|---|---|---|---|
attributes | arrayAdditional attributes that should be rendered on the component where ui:attributes is used. | No | [] |
asChild | booleanIf true the component uses its child only without the component template. Like Radix UI asChild or Base UI render props. | No | - |
class | stringThe CSS class(es) to be applied to the component. | No | - |
Rendered data attributes
| Attribute | Description |
|---|---|
data-max-length-behavior | The `maxLengthBehavior` while there is a `maxLength`: `block`, `warn` or `invalid`. Style the over-limit state of `warn` and `invalid` apart. One of: "invalid", "block", "warn". |
data-at-limit | Present while the value is exactly `maxLength` characters long. |
data-over-limit | Present while the value is longer than `maxLength` and `maxLengthBehavior` is not `block`. |
Machine options
Permalink to heading "Machine options"| Name | Description | Default |
|---|---|---|
id | stringThe unique identifier of the textarea. | - |
ids | Record<string, string> | undefinedThe ids of the parts other elements reference: textarea (the for of its label) and wordCount. | - |
name | string | undefinedThe name of the textarea, as it is submitted with the form. Taken from the closest field. | - |
disabled | boolean | undefinedWhether the textarea is disabled. Taken from the closest field. | - |
readOnly | boolean | undefinedWhether the textarea is read-only. Taken from the closest field. | - |
required | boolean | undefinedWhether the textarea is required. Taken from the closest field. | - |
invalid | boolean | undefinedWhether the textarea is invalid. Taken from the closest field. | - |
defaultValue | string | undefinedThe initial value of the textarea. | - |
maxLength | number | undefinedThe most characters allowed. It also drives the word count, e.g. 42 / 250 characters. | - |
maxLengthBehavior | MaxLengthBehavior | undefinedWhat happens when the value is longer than maxLength. block stops the typing and cuts a paste off, warn allows both and only shows that the value is over the limit, invalid does the same and makes a surrounding field invalid (tooLong). | "block" |
rows | number | undefinedThe number of visible lines. | - |
submitOn | TextareaSubmitOn | undefinedSubmits the closest <form> on a keypress instead of inserting a newline. 'enter' submits on Enter, Shift+Enter still inserts a newline. 'mod+enter' submits on Cmd or Ctrl+Enter, Enter always inserts a newline. By default Enter is never intercepted. | - |
translations | TextareaTranslations | undefinedThe localized texts of the textarea. | - |
announce | boolean | undefinedWhether the word count is announced to assistive tech. Set to false to stay silent. | true |
announceDebounce | number | undefinedMilliseconds to debounce word count announcements by, so rapid typing doesn't spam assistive tech on every keystroke. Set to 0 to announce every change immediately. | 600 |
transform | ((value: string, event: Event) => string) | undefinedRuns on every native input event, before the value is committed. Return the value to write back to the textarea, e.g. trimmed. The cursor position is kept. It cannot be set from a Fluid template, only by constructing Textarea in your own entry file. | - |
onValueChange | ((details: { value: string }) => void) | undefinedCalled when the value changes. | - |
Machine JavaScript API
Permalink to heading "Machine JavaScript API"| Name | Type | Description |
|---|---|---|
value | string | The current value of the textarea. |
count | number | The number of characters in the value. |
over | number | The number of characters over maxLength. It is always 0 for maxLengthBehavior="block". |
atLimit | boolean | Whether the value is exactly maxLength characters long. |
countText | string | null | The word count text, e.g. 42 / 250 characters. null without maxLength or when the text is turned off. |
disabled | boolean | Whether the textarea is disabled. |
readOnly | boolean | Whether the textarea is read-only. |
required | boolean | Whether the textarea is required. |
invalid | boolean | Whether the textarea is invalid. |
name | string | undefined | The name of the textarea. |
maxLength | number | undefined | The most characters allowed. |