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.


Sources

  1. Adobe HTL Style Guide - Core Components
  2. Adobe Experience League - HTL Java Use-API
  3. Adobe Experience League - Getting Started with HTL
  4. Adobe Experience League Community - Sling Models Best Practices