AEM: Best Practices for Component Development
AEM: Best Practices for Component Development
Component development in Adobe Experience Manager requires attention to several aspects: performance, maintainability, reusability. This guide is based on official Adobe documentation and verified community best practices.
1. HTL: Avoid Anti-Performance Patterns
❌ BAD: Repeating the same test multiple times
<h1 data-sly-test="${component.textEnabled}">${component.title}</h1>
<p data-sly-test="${component.textEnabled}">${component.description}</p>Problem: The test is executed twice, impacting performance.
✅ GOOD: Reuse test results
<h1 data-sly-test.textEnabled="${component.textEnabled}">${component.title}</h1>
<p data-sly-test="${textEnabled}">${component.description}</p>Source: Adobe HTL Style Guide - Core Components
2. HTL: Use <sly> Instead of <div> with unwrap
❌ BAD: Using div + unwrap
<div data-sly-include="content.html" data-sly-unwrap></div>
<div data-sly-resource="${item @ selectors='event'}" data-sly-unwrap></div>Problem: Unnecessary markup and less readable.
✅ GOOD: Use the <sly> tag
<sly data-sly-include="content.html"></sly>
<sly data-sly-resource="${item @ selectors = 'event'}"></sly>Source: Adobe HTL Style Guide
3. HTL: Attribute Order
❌ BAD: HTL attributes after HTML attributes
<h1 class="cmp-component__title" data-sly-test="${component.title}">
${component.title}
</h1>Problem: HTL variables might not be available in subsequent attributes.
✅ GOOD: HTL attributes before HTML attributes
<h1 data-sly-test="${component.title}" class="cmp-component__title">
${component.title}
</h1>Source: Adobe HTL Style Guide
4. HTL: Place data-sly-use at Top-Level
❌ BAD: data-sly-use in nested elements
<div class="cmp-teaser">
<h3 data-sly-use.teaser="com.example.models.Teaser">
${teaser.title}
</h3>
<p>${teaser.description}</p>
</div>Problem: Hard to see name clashing, risk of initializing objects twice.
✅ GOOD: data-sly-use at top level
<div data-sly-use.teaser="com.example.models.Teaser" class="cmp-teaser">
<h3>${teaser.title}</h3>
<p>${teaser.description}</p>
</div>Source: Adobe HTL Style Guide
5. Sling Models: Use Adobe Recommended Practice
Adobe recommends using Sling Models as best practice for AEM components since AEM 6.1+.
@Model(
adaptables = Resource.class,
defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL
)
public class TeaserModel {
@ValueMapValue
private String title;
@ValueMapValue
private String description;
public String getTitle() {
return title;
}
public String getDescription() {
return description;
}
}Sling Models Advantages:
- ✅ Testability: Easy to write unit tests
- ✅ Dependency Injection: Native and clean
- ✅ Reusability: Can be used outside HTL
- ✅ Maintainability: Cleaner and more readable code
Source: Adobe Experience League - HTL Java Use-API
When to use Sling Models vs direct properties?
- Direct properties (
${properties.title}): OK for very simple components without backend logic - Sling Models: Recommended when backend logic, data transformations, or integrations are needed
Source: Adobe Experience League Community
6. Separation Between Business Logic and Presentation
Adobe explicitly recommends:
"This method allows all complex business logic to be encapsulated in the Java code, while the HTL code deals only with direct markup production."
What goes in HTL:
- ✅ HTML markup
- ✅ Simple presentation logic (conditions, iterations)
- ✅ Variable interpolation
What goes in Java/Sling Models:
- ✅ Complex business logic
- ✅ Data calculations and transformations
- ✅ OSGi service integrations
Source: Adobe Experience League - HTL Java Use-API
7. Dialog Validation: Correct Syntax
❌ WRONG: validation="email" (doesn't exist)
<!-- This syntax DOES NOT WORK -->
<email
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
validation="email"/>✅ CORRECT: Use granite:data with regex
<email
jcr:primaryType="nt:unstructured"
sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
fieldLabel="Email"
name="./email"
required="{Boolean}true">
<granite:data
jcr:primaryType="nt:unstructured"
validation-regex="^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\.[a-zA-Z]{2,}$"
validation-regex-message="Please enter a valid email address"/>
</email>Note: Regex syntax varies between AEM 6.3 and 6.4+ for backslash escaping.
Sources:
8. HTL: Correct Comments
❌ BAD: HTML comments
<!-- Don't use HTML comments, they appear in final markup -->
<div>${component.title}</div>✅ GOOD: HTL comments
<!--/* Use HTL comments, they are removed from output */-->
<div>${component.title}</div>Source: Adobe HTL Style Guide
9. HTL: Simplify Ternary Expressions
❌ BAD: Redundant ternary
<div class="${cssClass ? cssClass : 'my-class'}"></div>✅ GOOD: Use OR operator
<div class="${cssClass || 'my-class'}"></div>Source: Adobe HTL Style Guide
10. HTL: Don't Use Getter Prefixes
❌ BAD: Calling getters explicitly
<p>${component.getTitle}</p>
<a href="${item.link}" data-sly-unwrap="${item.isActive}">...</a>✅ GOOD: Access properties directly
<p>${component.title}</p>
<a href="${item.link}" data-sly-unwrap="${item.active}">...</a>Source: Adobe HTL Style Guide
Conclusions
These best practices are verified and based on:
- ✅ Official Adobe Experience League documentation
- ✅ HTL Style Guide from Adobe Core Components
- ✅ Adobe Experience League Community
- ✅ Real implementations in Core Components
Applying them will allow you to write components that are more maintainable, performant, and compliant with Adobe standards.