Skip to main content

Textarea

A textarea lets users enter multiple lines of text, such as when answering an open-ended question.

<div class="ons-field">
  <label class="ons-label  ons-label--with-description " for="textarea">Please provide some feedback
  </label>
  <span id="description-hint" class="ons-label__description  ons-input--with-description">
    For example, describe any difficulties you experienced in the use of this service
  </span>
  <textarea id="textarea" class="ons-input ons-input--textarea   " name="feedback" rows="8"></textarea>
</div>
{% from "components/textarea/_macro.njk" import onsTextarea %}

{{
    onsTextarea({
        "id": "textarea",
        "name": "feedback",
        "label": {
            "text": "Please provide some feedback",
            "description": "For example, describe any difficulties you experienced in the use of this service"
        }
    })
}}
Name Type Required Description
id string true The id of the textarea. This will also be added to the label if a label is specified
classes string false Classes to add to the textarea
name string false The name of the textarea
value string false The value of the textarea
attributes object false HTML attributes (for example, data attributes) to add to the textarea
label Label (ref) false Settings for the input label. for will automatically be set to match the textarea id
rows number false The size of the text area in number of rows, defaults to 8
mutuallyExclusive MutuallyExclusive (ref) false Configuration object if this is a mutually exclusive list
CharCheckLimit CharCheckLimit false Configuration object if this input has a character count
fieldId string false Id for the field
fieldClasses string false Classes for the field
legend string Only if mutuallyExclusive set Text for the legend
legendClasses string false Classes for the legend
dontWrap boolean false Prevents the textarea from being wrapped in a field component
error Error (ref) false Configuration for validation errors

CharCheckLimit

Name Type Required Description
limit number false The maximum amount of characters a user should type in
charCountPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining. {x} Will be replaced with the number
charCountSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining (singular). {x} Will be replaced with the number
{% macro onsTextarea(params) %}
    {% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %}
    {% from "components/field/_macro.njk" import onsField %}
    {% from "components/label/_macro.njk" import onsLabel %}
    {% from "components/char-check-limit/_macro.njk" import onsCharLimit %}


    {% set field %}
        {% set textareaExlusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %}

        {{ onsLabel({
            "for": params.id,
            "text": params.label.text,
            "description": params.label.description,
            "classes": params.label.classes
        }) }}

        <textarea
            id="{{ params.id }}"
            class="ons-input ons-input--textarea {% if params.error is defined and params.error %} ons-input--error {% endif %}{% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %} ons-js-char-limit-input{% endif %}{{ textareaExlusiveClass }} {{ params.classes }} {% if params.width is defined and params.width %}ons-input--w-{{ params.width }}{% endif %}"
            name="{{ params.name }}"
            rows="{{ params.rows | default(8) }}"
            {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
                maxlength="{{ params.charCheckLimit.limit }}"
                data-char-limit-ref="{{ params.id }}-lim-remaining"
                aria-describedby="{{ params.id }}-lim-remaining"
            {% endif %}
            {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        >{{ params.value }}</textarea>

        {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
            {% call onsCharLimit({
                "id": params.id ~ "-lim",
                "limit": params.charCheckLimit.limit,
                "charCountSingular": params.charCheckLimit.charCountSingular,
                "charCountPlural": params.charCheckLimit.charCountPlural
            }) %}
            {% endcall %}
        {% endif %}
    {% endset %}

    {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
        {% call onsMutuallyExclusive({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "legend": params.legend,
            "legendClasses": params.legendClasses,
            "description": params.description,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "checkbox": params.mutuallyExclusive.checkbox,
            "or": params.mutuallyExclusive.or,
            "deselectMessage": params.mutuallyExclusive.deselectMessage,
            "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective,
            "deselectCheckboxAdjective": params.mutuallyExclusive.deselectCheckboxAdjective,
            "error": params.error
        }) %}
            {% call onsField({
                "error": params.error
            }) %}
                {{ field | safe }}
            {% endcall %}
        {% endcall %}
    {% else %}
        {% call onsField({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "error": params.error
        }) %}
            {{ field | safe }}
        {% endcall %}
    {% endif %}
{% endmacro %}

When to use this component

Use the textarea component when you need the user to enter text that’s longer than one line, for example, to answer an open-ended question.

A good use of this component is to ask the user for feedback about your service by means of the phase banner.

When not to use this component

Open-ended questions can be difficult for users to answer, so consider breaking down the question into simpler questions that can be answered using a single line input or by selecting an answer using, for example, radios.

How to use this component

All textarea components must have a visible label, with or without a description.

Textareas should be appropriately sized to help the user understand what they should enter by making them the right size for the content they are intended for.

Use appropriately sized textareas

Where possible, you should make the textareas proportional to the amount of text you expect users to enter.

By default, the textarea component will fill the width of its container and has a default height of 8 lines of text.

A width constraint class can be used to make the width of textarea smaller, or the rows parameter can be set to make the height of the textarea larger.

Variants

Character limit

The character limit counter is displayed when the charCheckLimit object is defined with params limit, charCountSingular, charCountPlural parameters.

<div class="ons-field">
  <label class="ons-label  ons-label--with-description " for="textarea-char-limit">Please provide some feedback
  </label>
  <span id="description-hint" class="ons-label__description  ons-input--with-description">
    For example, describe any difficulties you experienced in the use of this service
  </span>
  <textarea id="textarea-char-limit" class="ons-input ons-input--textarea  ons-js-char-limit-input  ons-input--w-30" name="feedback-limited" rows="8" maxlength="200" data-char-limit-ref="textarea-char-limit-lim-remaining" aria-describedby="textarea-char-limit-lim-remaining"></textarea>
  <span id="textarea-char-limit-lim-remaining" class="ons-input__limit ons-u-fs-s--b ons-u-d-no ons-u-mt-xs" data-charcount-singular="You have {x} character remaining" data-charcount-plural="You have {x} characters remaining" data-charcount-limit-singular="" data-charcount-limit-plural="">
  </span>
</div>
{% from "components/textarea/_macro.njk" import onsTextarea %}

{{
    onsTextarea({
        "id": "textarea-char-limit",
        "name": "feedback-limited",
        "width": "30",
        "label": {
            "text": "Please provide some feedback",
            "description": "For example, describe any difficulties you experienced in the use of this service"
        },
        "charCheckLimit": {
            "limit": 200,
            "charCountSingular": "You have {x} character remaining",
            "charCountPlural": "You have {x} characters remaining"
        }
    })
}}
Name Type Required Description
id string true The id of the textarea. This will also be added to the label if a label is specified
classes string false Classes to add to the textarea
name string false The name of the textarea
value string false The value of the textarea
attributes object false HTML attributes (for example, data attributes) to add to the textarea
label Label (ref) false Settings for the input label. for will automatically be set to match the textarea id
rows number false The size of the text area in number of rows, defaults to 8
mutuallyExclusive MutuallyExclusive (ref) false Configuration object if this is a mutually exclusive list
CharCheckLimit CharCheckLimit false Configuration object if this input has a character count
fieldId string false Id for the field
fieldClasses string false Classes for the field
legend string Only if mutuallyExclusive set Text for the legend
legendClasses string false Classes for the legend
dontWrap boolean false Prevents the textarea from being wrapped in a field component
error Error (ref) false Configuration for validation errors

CharCheckLimit

Name Type Required Description
limit number false The maximum amount of characters a user should type in
charCountPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining. {x} Will be replaced with the number
charCountSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining (singular). {x} Will be replaced with the number
{% macro onsTextarea(params) %}
    {% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %}
    {% from "components/field/_macro.njk" import onsField %}
    {% from "components/label/_macro.njk" import onsLabel %}
    {% from "components/char-check-limit/_macro.njk" import onsCharLimit %}


    {% set field %}
        {% set textareaExlusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %}

        {{ onsLabel({
            "for": params.id,
            "text": params.label.text,
            "description": params.label.description,
            "classes": params.label.classes
        }) }}

        <textarea
            id="{{ params.id }}"
            class="ons-input ons-input--textarea {% if params.error is defined and params.error %} ons-input--error {% endif %}{% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %} ons-js-char-limit-input{% endif %}{{ textareaExlusiveClass }} {{ params.classes }} {% if params.width is defined and params.width %}ons-input--w-{{ params.width }}{% endif %}"
            name="{{ params.name }}"
            rows="{{ params.rows | default(8) }}"
            {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
                maxlength="{{ params.charCheckLimit.limit }}"
                data-char-limit-ref="{{ params.id }}-lim-remaining"
                aria-describedby="{{ params.id }}-lim-remaining"
            {% endif %}
            {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        >{{ params.value }}</textarea>

        {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
            {% call onsCharLimit({
                "id": params.id ~ "-lim",
                "limit": params.charCheckLimit.limit,
                "charCountSingular": params.charCheckLimit.charCountSingular,
                "charCountPlural": params.charCheckLimit.charCountPlural
            }) %}
            {% endcall %}
        {% endif %}
    {% endset %}

    {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
        {% call onsMutuallyExclusive({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "legend": params.legend,
            "legendClasses": params.legendClasses,
            "description": params.description,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "checkbox": params.mutuallyExclusive.checkbox,
            "or": params.mutuallyExclusive.or,
            "deselectMessage": params.mutuallyExclusive.deselectMessage,
            "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective,
            "deselectCheckboxAdjective": params.mutuallyExclusive.deselectCheckboxAdjective,
            "error": params.error
        }) %}
            {% call onsField({
                "error": params.error
            }) %}
                {{ field | safe }}
            {% endcall %}
        {% endcall %}
    {% else %}
        {% call onsField({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "error": params.error
        }) %}
            {{ field | safe }}
        {% endcall %}
    {% endif %}
{% endmacro %}

How to check a textarea

To help users enter something in a required textarea, you should:

  • check they have entered something in the textarea
  • show an error message if they have not entered anything

Error messages

Use the correct errors pattern and show the error details above the textarea.

<div class="ons-panel ons-panel--error ons-panel--no-title ons-u-mb-s" id="feedback-error">
  <span class="ons-u-vh">Error: </span>
  <div class="ons-panel__body">
    <p class="ons-panel__error">
      <strong>Enter your feedback</strong>
    </p>
    <div class="ons-field">
      <label class="ons-label  ons-label--with-description " for="textarea">Please provide some feedback
      </label>
      <span id="description-hint" class="ons-label__description  ons-input--with-description">
        For example, describe any difficulties you experienced in the use of this service
      </span>
      <textarea id="textarea" class="ons-input ons-input--textarea  ons-input--error   " name="feedback" rows="8"></textarea>
    </div>
  </div>
</div>
{% from "components/textarea/_macro.njk" import onsTextarea %}

{{
    onsTextarea({
        "id": "textarea",
        "name": "feedback",
        "label": {
            "text": "Please provide some feedback",
            "description": "For example, describe any difficulties you experienced in the use of this service"
        },
            "error": {
                "id": "feedback-error",
                "text": "Enter your feedback",
                "dsExample": isPatternLib
            }
    })
}}
Name Type Required Description
id string true The id of the textarea. This will also be added to the label if a label is specified
classes string false Classes to add to the textarea
name string false The name of the textarea
value string false The value of the textarea
attributes object false HTML attributes (for example, data attributes) to add to the textarea
label Label (ref) false Settings for the input label. for will automatically be set to match the textarea id
rows number false The size of the text area in number of rows, defaults to 8
mutuallyExclusive MutuallyExclusive (ref) false Configuration object if this is a mutually exclusive list
CharCheckLimit CharCheckLimit false Configuration object if this input has a character count
fieldId string false Id for the field
fieldClasses string false Classes for the field
legend string Only if mutuallyExclusive set Text for the legend
legendClasses string false Classes for the legend
dontWrap boolean false Prevents the textarea from being wrapped in a field component
error Error (ref) false Configuration for validation errors

CharCheckLimit

Name Type Required Description
limit number false The maximum amount of characters a user should type in
charCountPlural string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining. {x} Will be replaced with the number
charCountSingular string false Required if CharCheckLimit is supplied. The string that will render how many characters are remaining (singular). {x} Will be replaced with the number
{% macro onsTextarea(params) %}
    {% from "components/mutually-exclusive/_macro.njk" import onsMutuallyExclusive %}
    {% from "components/field/_macro.njk" import onsField %}
    {% from "components/label/_macro.njk" import onsLabel %}
    {% from "components/char-check-limit/_macro.njk" import onsCharLimit %}


    {% set field %}
        {% set textareaExlusiveClass = " ons-js-exclusive-group-item" if params.mutuallyExclusive else "" %}

        {{ onsLabel({
            "for": params.id,
            "text": params.label.text,
            "description": params.label.description,
            "classes": params.label.classes
        }) }}

        <textarea
            id="{{ params.id }}"
            class="ons-input ons-input--textarea {% if params.error is defined and params.error %} ons-input--error {% endif %}{% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %} ons-js-char-limit-input{% endif %}{{ textareaExlusiveClass }} {{ params.classes }} {% if params.width is defined and params.width %}ons-input--w-{{ params.width }}{% endif %}"
            name="{{ params.name }}"
            rows="{{ params.rows | default(8) }}"
            {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
                maxlength="{{ params.charCheckLimit.limit }}"
                data-char-limit-ref="{{ params.id }}-lim-remaining"
                aria-describedby="{{ params.id }}-lim-remaining"
            {% endif %}
            {% if params.attributes is defined and params.attributes %}{% for attribute, value in (params.attributes.items() if params.attributes is mapping and params.attributes.items else params.attributes) %}{{ attribute }}{% if value is defined and value %}="{{ value }}"{% endif %} {% endfor %}{% endif %}
        >{{ params.value }}</textarea>

        {% if params.charCheckLimit is defined and params.charCheckLimit and params.charCheckLimit.limit is defined and params.charCheckLimit.limit %}
            {% call onsCharLimit({
                "id": params.id ~ "-lim",
                "limit": params.charCheckLimit.limit,
                "charCountSingular": params.charCheckLimit.charCountSingular,
                "charCountPlural": params.charCheckLimit.charCountPlural
            }) %}
            {% endcall %}
        {% endif %}
    {% endset %}

    {% if params.mutuallyExclusive is defined and params.mutuallyExclusive %}
        {% call onsMutuallyExclusive({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "legend": params.legend,
            "legendClasses": params.legendClasses,
            "description": params.description,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "checkbox": params.mutuallyExclusive.checkbox,
            "or": params.mutuallyExclusive.or,
            "deselectMessage": params.mutuallyExclusive.deselectMessage,
            "deselectGroupAdjective": params.mutuallyExclusive.deselectGroupAdjective,
            "deselectCheckboxAdjective": params.mutuallyExclusive.deselectCheckboxAdjective,
            "error": params.error
        }) %}
            {% call onsField({
                "error": params.error
            }) %}
                {{ field | safe }}
            {% endcall %}
        {% endcall %}
    {% else %}
        {% call onsField({
            "id": params.fieldId,
            "classes": params.fieldClasses,
            "dontWrap": params.dontWrap,
            "legendIsQuestionTitle": params.legendIsQuestionTitle,
            "error": params.error
        }) %}
            {{ field | safe }}
        {% endcall %}
    {% endif %}
{% endmacro %}

If the textarea is empty

Use “Enter [whatever type of text is being asked for]”.
For example, “Enter your feedback” or “Enter a job title”.

Research on this component

The textarea component was last user tested between February and September 2020 during research for Census 2021. The following findings were reported:

  • along with the size of the textarea, the character limit helps users know how long their answer should be
  • the character counter is often not noticed until the user starts to enter text
  • users that look down at a keyboard while they type may not notice the character counter
  • users prefer a limit on characters that can be entered as it prevents them entering far more text than is needed
  • the change from black to red text when the character limit is reached helps users notice the counter

Help improve this component

Let us know how we could improve this component or share your user research findings. Discuss the ‘Textarea’ component on GitHub