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- Alwaysgranite/ui/components/coral/foundation/form/textareafieldLabel- Label visible to the authorname- 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 resizablenone- 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 }
</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 }</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
- ✅ Appropriate rows: 3-5 for descriptions, 8-12 for long text
- ✅ Maxlength for SEO: 160 for meta description, 125 for alt text
- ✅ Descriptive emptyText: Give clear examples
- ✅ FieldDescription: Explain limits and usage
- ✅ Vertical resize: Allow resizing for long texts
- ✅ Preserve line breaks in HTL: Use
white-space: pre-lineor convert to<br> - ✅ Fallback to RTE: If formatting is needed, switch to RichText
- ✅ 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 →