TextArea allows for multi-line input.

also known as Multi-line Input

Figma:

Responsive:

Adaptive:

A11y:

Props

Component props
Name
Type
Default
id
Required
string
-

A unique identifier for the input.

onChange
Required
({
  event: SyntheticInputEvent<HTMLTextAreaElement>,
  value: string,
}) => void
-

Callback triggered when the value of the input changes.

dataTestId
string
-

Available for testing purposes, if needed. Consider better queries before using this prop.

disabled
boolean
false

Indicate if the input is currently disabled. See the disabled example for more details.

errorMessage
React.Node
-

For most use cases, pass a string with a helpful error message (be sure to localize!). In certain instances it can be useful to make some text clickable; to support this, you may instead pass a React.Node to wrap text in Link or TapArea. See the error message example for more details.

hasError
boolean
false

This field is deprecated and will be removed soon. Please do not use.

helperText
string
-

More information about how to complete the form field. See the helper text example for more details.

label
string
-

The label for the input. Be sure to localize the text.

labelDisplay
"visible" | "hidden"
"visible"

Whether the label should be visible or not. If hidden, the label is still available for screen reader users, but does not appear visually. See the label visibility variant for more info.

maxLength
{
  characterCount: number,
  errorAccessibilityLabel: string,
}
-

The maximum number of characters allowed in TextArea. maxLength must be an integer value 0 or higher. See the maximum length variant for more details.

name
string
-

A unique name for the input.

onBlur
({
  event: SyntheticFocusEvent<HTMLTextAreaElement>,
  value: string,
}) => void
-

Callback triggered when the user blurs the input.!

onFocus
({
  event: SyntheticFocusEvent<HTMLTextAreaElement>,
  value: string,
}) => void
-

Callback triggered when the user focuses the input.

onKeyDown
({
  event: SyntheticKeyboardEvent<HTMLTextAreaElement>,
  value: string,
}) => void
-

Callback triggered when the user presses any key while the input is focused.

placeholder
string
-

Placeholder text shown the the user has not yet input a value.

readOnly
boolean
false

Indicate if the input is currently readOnly. See the readOnly example for more details.

ref
React.Element<"input">
-

Ref that is forwarded to the underlying input element. See the ref example for more details.

rows
number
3

Number of text rows to display. Note that tags take up more space, and will show fewer rows than specified.

tags
$ReadOnlyArray<React.Element<typeof Tag>>
-

List of tags to display in the component. See the tags example for more details.

value
string
-

The current value of the input.

Usage guidelines

When to use
  • Allowing users to input long portions of free-form text while ensuring all text entered remains visible.
  • Allowing users to type free-form options that get converted into Tags within the TextArea.
When not to use
  • For inputs that expect a certain format, like a date or email. Use a DatePicker or TextField instead.

Best practices

Do

Use TextArea as an affordance to input longer-form text content — typically anything longer than a brief sentence.

Don't

Use TextArea when the text input is a single, non-sentence response — even in cases with long content. Use TextField instead.

Do

Use label to clearly denote what information the user should input. Use placeholder sparingly as they can erode usability of form fields.

Don't

Use placeholder as a replacement for label, as this creates accessibility and usability issues.

Do

Use helperText to provide additional context that will aid the user in most effectively inputing information.

Don't

Use placeholder to provide any information necessary to filling out the form field. Placeholder text disappears after the user begins entering data and should not contain crucial information.

Do

Set the height of TextArea using row to ensure that the typical amount of text entered will be visible without needing to scroll.

Don't

Set the row prop to less than 2. Use TextField when expecting only a single line of text.

Accessibility

Comprehension

Be sure to provide instructions to help users understand how to complete the form and use individual form controls.

Labels

Ensure the labels are precise and concise. Labels should only describe the text field they are associated with, and they must be visible. If you cannot use the label prop, ensure the alternative label's htmlFor attribute matches the TextArea's id. Labels are properly associated when clicking the label focuses the TextArea.

Validation

When providing a validation message, make sure the instructions are clear and help users complete the field. For example, "This field is required to submit". In addition, use the helper text to provide instructions to help users understand how to complete the text field or to indicate any needed input, allowed formats, timing limitations, or other pertinent information.

These practices give users of assistive technologies more information about the form, helping them to fill it out.

Keyboard navigation

TextArea has conventional keyboard support.

  • Users relying on the keyboard expect to move focus to each TextArea by using the tab key or shift+tab when moving backwards.
  • Setting disabled will prevent TextArea from receiving keyboard focus or input.

Autofocus

TextArea intentionally lacks support for autofocus. Generally speaking, autofocus interrupts normal page flow for screen readers making it an anti-pattern for accessibility.

onSubmit

TextArea is commonly used as an input in forms alongside submit buttons. In these cases, users expect that pressing Enter or Return with the input focused will submit the form.

Out of the box, TextArea doesn't expose an onSubmit handler or individual key event handlers due to the complexities of handling these properly. Instead, developers are encouraged to wrap TextField in a <form> with an onSubmit handler.

Localization

Be sure to localize all text strings. Note that localization can lengthen text by 20 to 30 percent.

Variants

Default

TextArea will expand to fill the width of the parent container by default.

import { useState } from 'react';
import { Box, TextArea } from 'gestalt';

export default function Example() {
  const [value, setValue] = useState('');
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box width="100%">
        <TextArea
          id="aboutme"
          label="About me"
          onChange={(event) => setValue(event.value)}
          placeholder="Write something about yourself..."
          value={value}
        />
      </Box>
    </Box>
  );
}

Helper text

Whenever you want to provide more information about a form field, you should use helperText.

import { useState } from 'react';
import { Box, TextArea } from 'gestalt';

export default function Example() {
  const [value, setValue] = useState('');
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box width="100%">
        <TextArea
          helperText="Describe your favorite hobbies, foods, or books."
          id="aboutmemore"
          label="About me"
          onChange={(e) => setValue(e.value)}
          placeholder="Write something about yourself..."
          value={value}
        />
      </Box>
    </Box>
  );
}

Label visibility

In some cases, the label for a TextArea is represented in a different way visually, as demonstrated below. In these instances, you can set labelDisplay="hidden" to ensure TextArea is properly labeled for screen readers while using a different element to represent the label visually.

import { Box, Flex, Text, TextArea } from 'gestalt';

export default function Example() {
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Flex direction="column" gap={{ column: 2, row: 0 }} width="100%">
        <Text size="300" weight="bold">
          About me
        </Text>
        <TextArea
          id="textareaexampleHiddenLabel"
          label="About me"
          labelDisplay="hidden"
          onChange={() => {}}
          placeholder="Write something about yourself..."
        />
      </Flex>
    </Box>
  );
}

Read-only

TextArea can be in read-only mode in order to present information to the user without allowing them to edit the content. Typically this variation is used to show content or information that the user does not have permission or access to edit.

import { useState } from 'react';
import { Box, TextArea } from 'gestalt';

export default function Example() {
  const [value, setValue] = useState(
    'To keep shopping inspirational and actionable, we set high standards for our Merchants. Your website was not approved due to fuzzy, low quality images.'
  );
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box width="100%">
        <TextArea
          id="aboutmereadonly"
          label="Current errors"
          onChange={(data) => setValue(data.value)}
          readOnly
          value={value}
        />
      </Box>
    </Box>
  );
}

Disabled

TextArea can be disabled to indicate the user is unable to interact with it, either by mouse or keyboard. Disabled fields do not need to pass contrast requirements, so do not use a disabled TextArea to present information to the user (use readOnly instead).

import { useState } from 'react';
import { Box, TextArea } from 'gestalt';

export default function Example() {
  const [value, setValue] = useState('');
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box width="100%">
        <TextArea
          disabled
          id="disabled"
          label="About me"
          onChange={(e) => setValue(e.value)}
          placeholder="Write something about yourself..."
          value={value}
        />
      </Box>
    </Box>
  );
}

Error message

TextArea can display an error message. Simply pass in an errorMessage when there is an error present and TextArea will handle the rest.

Don't use errorMessage to provide feedback on character count errors. See the maximum length variant for more details.

import { useState } from 'react';
import { Box, TextArea } from 'gestalt';

export default function Example() {
  const [value, setValue] = useState('');
  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box width="100%">
        <TextArea
          errorMessage={!value ? "This field can't be blank!" : null}
          id="witherror"
          label="About me"
          onChange={(e) => setValue(e.value)}
          placeholder="Write something about yourself..."
          value={value}
        />
      </Box>
    </Box>
  );
}

Maximum length

TextArea supports the native maxlength input attribute. maxLength sets the maximum number of characters allowed to be entered by the user in TextArea. maxLength must be an integer value 0 or higher.

The user cannot exceed the maximum number of characters interacting with the component. Whenever possible, avoid setting initial values from the parent component's state that already exceed the maxLength.

When maxLength is passed to TextArea, the component displays a character counter as well as a warning or problem Status when the user reaches or the prepopulated controlled value exceeds the maximum length of characters.

The first example shows an empty TextArea with maxLength set to 200 characters. The second example shows the warning and problem Status.

import { useState } from 'react';
import { Box, TextArea } from 'gestalt';

export default function TextAreaExample() {
  const [value, setValue] = useState('');
  const characterCount = 200;

  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <TextArea
        helperText="Describe your image with detail so visually impaired users can understand your Pin"
        id="maxLength"
        label="Alt text"
        maxLength={{
          characterCount,
          errorAccessibilityLabel:
            'Limit reached. You can only use 200 characters in this field.',
        }}
        onBlur={() => {}}
        onChange={(e) => setValue(e.value)}
        onFocus={() => {}}
        placeholder="Enter the image alt text"
        rows={4}
        value={value}
      />
    </Box>
  );
}

import { useState } from 'react';
import { Box, Flex, TextArea } from 'gestalt';

export default function TextAreaExample() {
  const [valueA, setValueA] = useState(
    'Before and after via side by side display of pale woman with auburn hair using concealer. The image shows her using a brush with concealer under her eyes.  The second image shows her with full makeup.'
  );
  const [valueB, setValueB] = useState(
    'Before and after via side by side display of pale woman with auburn hair using concealer. The image shows her using a brush with concealer under her eyes, second image shows her with full makeup and says new.'
  );

  const characterCount = 200;
  const errorAccessibilityLabel =
    'Limit reached. You can only use 200 characters in this field.';

  return (
    <Box display="flex" height="100%" justifyContent="center" padding={8}>
      <Flex direction="column" gap={12}>
        <TextArea
          helperText="Describe your image with detail so visually impaired users can understand your Pin"
          id="maxLengthReached"
          label="Alt text"
          maxLength={{ characterCount, errorAccessibilityLabel }}
          onBlur={() => {}}
          onChange={({ value }) => setValueA(value)}
          onFocus={() => {}}
          placeholder="Enter the image alt text"
          rows={4}
          value={valueA}
        />
        <TextArea
          helperText="Describe your image with detail so visually impaired users can understand your Pin"
          id="maxLengthExceeded"
          label="Alt text"
          maxLength={{ characterCount, errorAccessibilityLabel }}
          onBlur={() => {}}
          onChange={({ value }) => setValueB(value)}
          onFocus={() => {}}
          placeholder="Enter the image alt text"
          rows={4}
          value={valueB}
        />
      </Flex>
    </Box>
  );
}

With tags

You can include Tag elements in the input using the tags prop. You can use the rows prop to limit the number of lines for tags.

Note that the TextArea component does not internally manage tags. That should be handled in the application state through the component's event callbacks. We recommend creating new tags on enter key presses, and removing them on backspaces when the cursor is in the beginning of the field. We also recommend filtering out empty tags.

This example showcases the recommended behavior.

import { useRef, useState } from 'react';
import { Box, Tag, TextArea } from 'gestalt';

const CITIES = ['San Francisco', 'New York'];

export default function Example() {
  const [value, setValue] = useState('');
  const [tags, setTags] = useState(CITIES);

  const ref = useRef(null);

  const onChangeTagManagement = (e) => {
    // Create new tags around new lines
    const tagInput = e.value.split(/\\n+/);
    if (tagInput.length > 1) {
      setTags([
        ...tags,
        // Avoid creating a tag on content on the last line, and filter out
        // empty tags
        ...tagInput.splice(0, tagInput.length - 1).filter((val) => val !== ''),
      ]);
    }
    setValue(tagInput[tagInput.length - 1]);
  };

  const onKeyDownTagManagement = ({
    event: {
      keyCode,
      currentTarget: { selectionEnd },
    },
  }) => {
    if (keyCode === 8 /* Backspace */ && selectionEnd === 0) {
      // Remove tag on backspace if the cursor is at the beginning of the field
      setTags([...tags.slice(0, -1)]);
    }
  };

  const renderedTags = tags.map((tag, idx) => (
    <Tag
      key={tag}
      accessibilityRemoveIconLabel={`Remove ${tag} tag`}
      onRemove={() => {
        const newTags = [...tags];
        newTags.splice(idx, 1);
        setTags([...newTags]);
        ref.current?.focus();
      }}
      text={tag}
    />
  ));

  return (
    <Box
      alignItems="center"
      display="flex"
      height="100%"
      justifyContent="center"
      padding={8}
    >
      <Box width="100%">
        <TextArea
          ref={ref}
          id="cities"
          label="Cities"
          onChange={onChangeTagManagement}
          onKeyDown={onKeyDownTagManagement}
          placeholder={
            value.length > 0 || tags.length > 0 ? '' : "Cities you've lived in"
          }
          tags={renderedTags}
          value={value}
        />
      </Box>
    </Box>
  );
}

With rows

The rows prop sets the number of rows shown in TextArea. The input will show a scrollbar if the content exceeds the rows limit.

import { useState } from 'react';
import { Box, Flex, NumberField, TextArea } from 'gestalt';

export default function Example() {
  const [value, setValue] = useState('');
  const [rows, setRows] = useState(2);

  return (
    <Box display="flex" height="100%" justifyContent="center" padding={8}>
      <Flex direction="column" gap={4} width="100%">
        <Box width={120}>
          <NumberField
            id="numberfield_rows"
            label="Number of Rows"
            onChange={(data) => {
              if (data.value) setRows(data.value);
            }}
            value={rows}
          />
        </Box>
        <TextArea
          id="rows"
          label="Rows example"
          onChange={(data) => {
            setValue(data.value);
          }}
          placeholder={`this text area has ${rows} rows`}
          rows={rows}
          value={value}
        />
      </Flex>
    </Box>
  );
}

Component quality checklist

Component quality checklist
Quality item
Status
Status description
Figma Library
Ready
Component is available in Figma for web and mobile web.
Responsive Web
Ready
Component responds to changing viewport sizes in web and mobile web.

TextField
TextField is ideal for short-form, single answer text input.

Tag
Tag can be used in conjunction with TextArea to display separate elements of content.

ComboBox
ComboBox + Tag is the recommended alternative to TextArea + Tag when selecting from a finite list list of items.