AEM Dialog Components #2: Textarea - Multi-Line Text Input

AEM Dialog Components #2: Textarea

What is a Textarea?

The Textarea is a Granite UI component that allows authors to enter multi-line text in AEM component dialogs.

Difference with Textfield:

  • Textfield → Single line
  • Textarea → Multiple lines, with line breaks

Common use cases:

  • Descriptions and abstracts
  • Notes and comments
  • Metadata and alt text
  • Simple code snippets
  • Disclaimers and legal notices

Basic Configuration

Minimal XML Dialog

<description
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Description"
    name="./description"/>

Essential properties:

  • sling:resourceType - Always granite/ui/components/coral/foundation/form/textarea
  • fieldLabel - Label visible to the author
  • name - JCR path where to save the value (always use ./)

Main Properties

1. Required (Mandatory Field)

<description
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Description"
    name="./description"
    required="{Boolean}true"/>

2. Rows (Height in Lines)

<description
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Description"
    name="./description"
    rows="{Long}5"/>

Default: 2 rows

Best practice:

  • 3-5 rows for short descriptions
  • 8-12 rows for long text
  • 15+ rows for code or extended content

3. Maxlength (Maximum Length)

<metaDescription
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Meta Description"
    name="./metaDescription"
    maxlength="{Long}160"
    rows="{Long}3"
    fieldDescription="Max 160 characters for SEO"/>

Important: Like textfield, use {Long} for the type hint!


4. EmptyText (Placeholder)

<notes
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Internal Notes"
    name="./notes"
    emptyText="Add notes for editors..."
    rows="{Long}4"/>

5. Resize (Resizing)

<content
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Content"
    name="./content"
    resize="vertical"
    rows="{Long}8"/>

Values:

  • vertical - Vertically resizable (default)
  • both - Vertically and horizontally resizable
  • none - Fixed size

Practical Examples

Example 1: Meta Description (SEO)

<metaDescription
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Meta Description"
    name="./metaDescription"
    fieldDescription="SEO description (max 160 chars)"
    required="{Boolean}true"
    maxlength="{Long}160"
    rows="{Long}3"
    emptyText="Enter a concise description for search engines..."/>

Use case: Page SEO optimization.


Example 2: Alt Text for Images

<altText
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Alt Text"
    name="./altText"
    fieldDescription="Describe the image for accessibility"
    required="{Boolean}true"
    maxlength="{Long}125"
    rows="{Long}2"
    emptyText="Describe what is shown in the image"/>

Use case: Accessibility and SEO for images.


Example 3: Internal Notes for Editors

<editorNotes
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Editor Notes"
    name="./editorNotes"
    fieldDescription="Internal notes (not displayed on the site)"
    rows="{Long}5"
    emptyText="Add notes for other editors..."/>

Use case: Workflow and communication between authors.


Example 4: Product Description

<productDescription
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Product Description"
    name="./productDescription"
    required="{Boolean}true"
    rows="{Long}8"
    maxlength="{Long}500"
    emptyText="Enter a detailed product description..."
    resize="vertical"/>

Use case: E-commerce, product sheets.


Example 5: Embed Code (Script, iframe)

<embedCode
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Embed Code"
    name="./embedCode"
    fieldDescription="Paste third-party embed code (YouTube, Twitter, etc.)"
    rows="{Long}10"
    resize="vertical"
    emptyText="<iframe src='...'></iframe>"/>

Use case: Integration of external content (videos, social, maps).


Complete Dialog with Textarea

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/1.0"
    xmlns:granite="http://www.adobe.com/jcr/granite/1.0"
    xmlns:cq="http://www.day.com/jcr/cq/1.0"
    xmlns:jcr="http://www.jcp.org/jcr/1.0"
    xmlns:nt="http://www.jcp.org/jcr/nt/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="Content Card"
    sling:resourceType="cq/gui/components/authoring/dialog">
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/container">
        <items jcr:primaryType="nt:unstructured">
            <tabs
                jcr:primaryType="nt:unstructured"
                sling:resourceType="granite/ui/components/coral/foundation/tabs">
                <items jcr:primaryType="nt:unstructured">

                    <!-- Content Tab -->
                    <content
                        jcr:primaryType="nt:unstructured"
                        jcr:title="Content"
                        sling:resourceType="granite/ui/components/coral/foundation/container">
                        <items jcr:primaryType="nt:unstructured">

                            <!-- Title (Textfield) -->
                            <title
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                fieldLabel="Title"
                                name="./title"
                                required="{Boolean}true"/>

                            <!-- Description (Textarea) -->
                            <description
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                fieldLabel="Description"
                                name="./description"
                                rows="{Long}5"
                                maxlength="{Long}300"
                                emptyText="Enter card description..."/>

                        </items>
                    </content>

                    <!-- SEO Tab -->
                    <seo
                        jcr:primaryType="nt:unstructured"
                        jcr:title="SEO"
                        sling:resourceType="granite/ui/components/coral/foundation/container">
                        <items jcr:primaryType="nt:unstructured">

                            <!-- Meta Description -->
                            <metaDescription
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                fieldLabel="Meta Description"
                                name="./metaDescription"
                                fieldDescription="SEO description (max 160 chars)"
                                maxlength="{Long}160"
                                rows="{Long}3"/>

                            <!-- Alt Text -->
                            <altText
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                fieldLabel="Image Alt Text"
                                name="./altText"
                                maxlength="{Long}125"
                                rows="{Long}2"/>

                        </items>
                    </seo>

                </items>
            </tabs>
        </items>
    </content>
</jcr:root>

Usage in HTL

Simple Value Reading

<div class="card-description">
  ${properties.description}
</div>

Important: The text will have line breaks (\n) that HTML ignores. To preserve them:

<div class="card-description" style="white-space: pre-line;">
  ${properties.description}
</div>

Or convert \n to <br>:

<div class="card-description">
  ${properties.description @ context='html'}
</div>

With Fallback

<div data-sly-test="${properties.description}" class="description">
  ${properties.description}
</div>

<!-- Or with ternary operator -->
<div class="description">
  ${properties.description || 'No description available'}
</div>

Truncate Text with CSS

<div class="description truncate-3-lines">
  ${properties.description}
</div>

CSS:

.truncate-3-lines {
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  text-overflow: ellipsis;
}

With Sling Model

@Model(adaptables = Resource.class)
public class ContentCardModel {

    @ValueMapValue
    private String title;

    @ValueMapValue
    private String description;

    @ValueMapValue
    private String metaDescription;

    public String getTitle() {
        return title;
    }

    public String getDescription() {
        return description;
    }

    /**
     * Truncated description to N characters for preview
     */
    public String getShortDescription(int maxLength) {
        if (description == null || description.length() <= maxLength) {
            return description;
        }
        return description.substring(0, maxLength) + "...";
    }

    /**
     * Meta description with fallback on description
     */
    public String getMetaDescription() {
        if (metaDescription != null && !metaDescription.isEmpty()) {
            return metaDescription;
        }
        // Fallback: use first 160 characters of description
        return getShortDescription(160);
    }

    /**
     * Converts \n to <br> for HTML
     */
    public String getDescriptionAsHtml() {
        if (description == null) {
            return "";
        }
        return description.replace("\n", "<br>");
    }

    public boolean hasDescription() {
        return description != null && !description.isEmpty();
    }
}

HTL with Model:

<div data-sly-use.model="com.mysite.models.ContentCardModel" class="card">

  <h3 class="card-title">${model.title}</h3>

  <!-- Full description -->
  <div data-sly-test="${model.hasDescription}" class="card-description">
    ${model.description}
  </div>

  <!-- Or truncated description -->
  <div class="card-preview">
    ${model.getShortDescription(150)}
  </div>

  <!-- Meta tag for SEO (in <head>) -->
  <meta name="description" content="${model.metaDescription}"/>

</div>

Textarea vs RichText Editor

When to Use Textarea

Use Textarea when:

  • Plain text without formatting
  • Strict length limits (e.g., meta description)
  • Performance is important
  • Bold, italic, links, etc. are not needed
  • Alt text, notes, metadata

When to Use RichText Editor

Use RichText when:

  • Formatting is needed (bold, italic, lists)
  • Links need to be inserted
  • Long editorial content
  • Authors need flexibility

Rule of thumb: If the author doesn't need formatting, use Textarea!


Advanced Properties

1. autofocus

<description
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Description"
    name="./description"
    autofocus="{Boolean}true"/>

Automatic focus when opening the dialog.


2. disabled and readOnly

<!-- Disabled -->
<generatedText
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Auto-Generated Text"
    name="./generatedText"
    disabled="{Boolean}true"
    rows="{Long}4"/>

<!-- ReadOnly -->
<originalText
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Original Text"
    name="./originalText"
    readOnly="{Boolean}true"
    rows="{Long}6"/>

3. granite:class

<customTextarea
    jcr:primaryType="nt:unstructured"
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    fieldLabel="Custom Textarea"
    name="./customTextarea"
    granite:class="monospace-font"
    rows="{Long}10"/>

Add custom CSS (e.g., monospace for code).


Common Issues

❌ Problem 1: Line Breaks Not Displayed

<!-- WRONG - HTML ignores \n -->
<div>${properties.description}</div>

<!-- CORRECT - Preserve line breaks -->
<div style="white-space: pre-line;">
  ${properties.description}
</div>

<!-- OR convert \n to <br> in Sling Model -->
<div>${model.descriptionAsHtml @ context='html'}</div>

❌ Problem 2: Maxlength Ignored

<!-- WRONG -->
<description maxlength="160"/>

<!-- CORRECT -->
<description maxlength="{Long}160"/>

Always use {Long} for numbers.


❌ Problem 3: Textarea Too Small

<!-- WRONG - default 2 rows, too small -->
<description
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    name="./description"/>

<!-- CORRECT - set appropriate rows -->
<description
    sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
    name="./description"
    rows="{Long}5"/>

❌ Problem 4: Text Visually Truncated

If text appears truncated in the component but is complete in JCR:

Cause: CSS limiting the container height Solution: Check CSS and add overflow: auto or white-space: pre-line


Best Practices

  1. Appropriate rows: 3-5 for descriptions, 8-12 for long text
  2. Maxlength for SEO: 160 for meta description, 125 for alt text
  3. Descriptive emptyText: Give clear examples
  4. FieldDescription: Explain limits and usage
  5. Vertical resize: Allow resizing for long texts
  6. Preserve line breaks in HTL: Use white-space: pre-line or convert to <br>
  7. Fallback to RTE: If formatting is needed, switch to RichText
  8. Validation in Sling Model: Check length and content server-side

When NOT to Use Textarea

Don't use textarea when:

  • Single line is needed → Use Textfield
  • Formatting is needed (bold, links) → Use RichText Editor
  • Complex code is needed → Use Code Editor (custom)
  • Selection from list is needed → Use Select or Radio

Next Lesson

In the next lesson we will cover the Checkbox component for boolean selections.

Resources:


Guide #2 of the AEM Dialog Components series - ← Previous lesson | Next lesson →