Props
Usage guidelines
- 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.
- For inputs that expect a certain format, like a date or email. Use a DatePicker or TextField instead.
Best practices
Use TextArea as an affordance to input longer-form text content — typically anything longer than a brief sentence.
Use TextArea when the text input is a single, non-sentence response — even in cases with long content. Use TextField instead.
Use label
to clearly denote what information the user should input. Use placeholder
sparingly as
they can erode usability of form fields.
Use placeholder
as a replacement for label
, as this creates accessibility and usability issues.
Use helperText
to provide additional context that will aid the user in most effectively inputing information.
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.
Set the height of TextArea using row
to ensure that the typical amount of text entered will be visible without needing to scroll.
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.
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
State
Enabled
The enabled state of TextArea that represents it can be interacted with.Error
TextArea can display an error message. Simply pass in anerrorMessage
when there is an error present and TextArea will handle the rest. Don't useerrorMessage
to provide feedback on character count errors. See the maximum length variant for more details.Read-only
Read-only TextAreas are used to present information to the user without allowing them to edit the content. Typically they are used to show content or information that the user does not have permission or access to edit.Disabled
TextAreas cannot be interacted with using the mouse or keyboard. They also do not need to meet contrast requirements, so do not use them to present info to the user (use "readOnly" instead).
import { useState } from 'react'; import { Box, TextArea } from 'gestalt'; export default function Example() { const [input, setInput] = useState(''); return ( <Box padding={8} width="100%"> <TextArea id="aboutme" label="About me" onChange={({ value }) => setInput(value)} placeholder="Write something about yourself..." value={input} /> </Box> ); }
import { useState } from 'react'; import { Box, TextArea } from 'gestalt'; export default function Example() { const [input, setInput] = useState(''); return ( <Box padding={8} width="100%"> <TextArea disabled id="disabled" label="About me" onChange={({ value }) => setInput(value)} placeholder="Write something about yourself..." value={input} /> </Box> ); }
import { useState } from 'react'; import { Box, TextArea } from 'gestalt'; export default function Example() { const [input, setInput] = useState(''); return ( <Box padding={8} width="100%"> <TextArea errorMessage={!input ? "This field can't be blank!" : null} id="witherror" label="About me" onChange={({ value }) => setInput(value)} placeholder="Write something about yourself..." value={input} /> </Box> ); }
import { useState } from 'react'; import { Box, TextArea } from 'gestalt'; export default function Example() { const [input, setInput] = 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 padding={8} width="100%"> <TextArea id="aboutmereadonly" label="Current errors" onChange={({ value }) => setInput(value)} readOnly value={input} /> </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 [input, setInput] = useState(''); return ( <Box padding={8} width="100%"> <TextArea helperText="Describe your favorite hobbies, foods, or books." id="aboutmemore" label="About me" onChange={({ value }) => setInput(value)} placeholder="Write something about yourself..." value={input} /> </Box> ); }
Label
'label' is an optional prop; however, TextArea should always be properly labelled. Learn about accessibility best practices regarding labels.
- Built-in label. Preferred. Consistent TextArea design and tested accessibility.
In some cases, the label for a TextArea is represented in a different way visually, as demonstrated below. We can take 2 approaches in this case.
Labelled TextArea (Label + TextArea). This is the best approach when a custom label is needed. The label focuses the TextArea when pressed.
Hidden built-in label (Label + TextArea). This is the best approach when there's significant visual distance between the label and the input. You can set
labelDisplay="hidden"
to ensure TextArea is properly labeled for screen readers while using a different element to represent the label visually. The 'visual' label doesn't focus the TextArea when pressed.
import { useState } from 'react'; import { Box, TextArea } from 'gestalt'; export default function Example() { const [input, setInput] = useState(''); return ( <Box padding={8} width="100%"> <TextArea id="textareaexampleHiddenLabel" label="About me" onChange={({ value }) => setInput(value)} placeholder="Write something about yourself..." value={input} /> </Box> ); }
import { useState } from 'react'; import { Box, Flex, Label, Text, TextArea } from 'gestalt'; export default function Example() { const [input, setInput] = useState(''); return ( <Box padding={8} width="100%"> <Flex direction="column" gap={2} width="100%"> <Label htmlFor="textareaExternalLabel"> <Text size="300" weight="bold"> About me </Text> </Label> <TextArea id="textareaExternalLabel" label="About me" labelDisplay="hidden" onChange={({ value }) => setInput(value)} placeholder="Write something about yourself..." value={input} /> </Flex> </Box> ); }
import { useState } from 'react'; import { Avatar, Box, Flex, Text, TextArea } from 'gestalt'; export default function Example() { const [input, setInput] = useState(''); return ( <Box padding={8} width="100%"> <Flex direction="column" gap={2} width="100%"> <Text size="300" weight="bold"> About me </Text> <Flex alignItems="center" gap={2} width="100%"> <Avatar accessibilityLabel="Shanice, Verified account" name="Shanice" size="lg" src="https://i.ibb.co/7tGKGvb/shanice.jpg" verified /> <Text size="300" weight="bold"> Shanice </Text> </Flex> <TextArea id="textareaexampleHiddenLabel" label="About me" labelDisplay="hidden" onChange={({ value }) => setInput(value)} placeholder="Write something about yourself..." value={input} /> </Flex> </Box> ); }
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.
function _nullishCoalesce(lhs, rhsFn) { if (lhs != null) { return lhs; } else { return rhsFn(); } } import { useState } from 'react'; import { Box, Flex, Label, NumberField, Switch, Text, TextArea } from 'gestalt'; export default function Example() { const [input, setInput] = useState(''); const [rows, setRows] = useState(4); const [display, setDisplay] = useState(false); const content = 'Nam consequat vehicula est, et congue turpis tempus quis. Vivamus sed iaculis elit, eu pharetra velit. Proin vitae arcu ut ligula sodales euismod at at sem. Nulla sit amet rutrum turpis. Ut fermentum congue hendrerit. Quisque lectus nisl, dignissim a eros id, ultrices varius libero. Nulla ultrices purus a nibh consectetur hendrerit. Proin a finibus est. Phasellus pharetra volutpat risus, at placerat libero venenatis in. Nunc id lacus posuere, fermentum dolor eu, ornare tellus. Sed quis tincidunt lorem, ut sollicitudin nunc. Cras cursus eget dolor at rutrum. Aliquam eu congue massa. Praesent vehicula ipsum tortor, vitae commodo odio sollicitudin non. Phasellus eget odio et nulla pretium vulputate. Donec vitae sem ac urna malesuada elementum.'; return ( <Box padding={8} width="100%"> <Flex direction="column" gap={6} width="100%"> <Flex gap={2}> <Switch id="display" onChange={() => setDisplay((value) => !value)} switched={display} /> <Box flex="grow" paddingX={2}> <Label htmlFor="display"> <Text>{display ? 'Show placeholder' : 'Show content'}</Text> </Label> </Box> </Flex> <TextArea id="rows1" label="Label 1" onChange={({ value }) => setInput(value)} placeholder="This textarea has 1 rows" rows={1} value={display ? content : input} /> <TextArea id="rows2" label="Label 2" onChange={({ value }) => setInput(value)} placeholder="This textarea has 2 rows" rows={2} value={display ? content : input} /> <TextArea id="rows3" label="Label 3" onChange={({ value }) => setInput(value)} placeholder="This textarea has 3 rows" rows={3} value={display ? content : input} /> <Flex gap={4} width="100%"> <NumberField id="numberfield rows" label="Number of Rows" min={2} onChange={({ value }) => setRows(_nullishCoalesce(value, () => 2))} value={rows} /> <TextArea id="dynamix rows" label="Label" onChange={({ value }) => setInput(value)} placeholder={`This textarea has ${rows} rows`} rows={rows} value={display ? content : input} /> </Flex> </Flex> </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 [input, setInput] = useState(''); return ( <Box padding={8} width="100%"> <TextArea helperText="Describe your image with detail so visually impaired users can understand your Pin" id="maxLength" label="Alt text" maxLength={{ characterCount: 200, errorAccessibilityLabel: 'Limit reached. You can only use 200 characters in this field.', }} onBlur={() => {}} onChange={({ value }) => setInput(value)} onFocus={() => {}} placeholder="Enter the image alt text" rows={4} value={input} /> </Box> ); }
import { useState } from 'react'; import { Box, Flex, TextArea } from 'gestalt'; export default function TextAreaExample() { const [inputA, setInputA] = 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 [inputB, setInputB] = 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 padding={8} width="100%"> <Flex direction="column" gap={6} width="100%"> <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 }) => setInputA(value)} onFocus={() => {}} placeholder="Enter the image alt text" rows={4} value={inputA} /> <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 }) => setInputB(value)} onFocus={() => {}} placeholder="Enter the image alt text" rows={4} value={inputB} /> </Flex> </Box> ); }
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, Flex, Tag, TextArea } from 'gestalt'; export default function Example() { const [value, setValue] = useState(''); const [tags, setTags] = useState(['a@pinterest.com', 'b@pinterest.com']); const ref = useRef(null); const onChangeTagManagement = (e) => { // Create new tags around spaces, commas, and semicolons. const tagInput = e.value.split(/[\\s,;]+/); if (tagInput.length > 1) { setTags([ ...tags, // Avoid creating a tag on content after the separators, 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)]); } else if (keyCode === 13 /* Enter */ && value.trim() !== '') { // Create a new tag on enter setTags([...tags, value.trim()]); setValue(''); } }; const renderedTags = tags.map((tag, idx) => ( <Tag key={tag} accessibilityRemoveIconLabel={`Remove ${tag} tag`} onRemove={() => { const newTags = [...tags]; newTags.splice(idx, 1); setTags([...newTags]); if (ref.current) { ref.current.focus(); } }} text={tag} /> )); return ( <Box padding={8} width="100%"> <Flex direction="column" gap={4}> <TextArea ref={ref} id="variants-tags" label="Emails" onChange={onChangeTagManagement} onKeyDown={onKeyDownTagManagement} tags={renderedTags} value={value} /> <TextArea ref={ref} disabled id="variants-tags" label="Emails" onChange={onChangeTagManagement} onKeyDown={onKeyDownTagManagement} tags={renderedTags} value={value} /> <TextArea ref={ref} id="variants-tags" label="Emails" onChange={onChangeTagManagement} onKeyDown={onKeyDownTagManagement} readOnly tags={renderedTags} value={value} /> <TextArea ref={ref} errorMessage="Select minimum of five" id="variants-tags" label="Emails" onChange={onChangeTagManagement} onKeyDown={onKeyDownTagManagement} tags={renderedTags} value={value} /> </Flex> </Box> ); }
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. |
Related
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.