HTL (HTML Template Language)

Master HTL — AEM's templating language. Learn the syntax, best practices, and how it keeps your templates clean and secure.

Last reviewed: December 2024

·AEM 6.1+AEM 6.5AEM as a Cloud Service
✅
Quick Understanding: HTL (formerly Sightly) is AEM's server-side templating language. It's HTML with special data-sly-* attributes that let you output dynamic content. It's intentionally simple — if you need complex logic, you use a Sling Model.

🔍 What Is It?

HTL stands for HTML Template Language. It is:

  • →AEM's recommended templating language (replaced JSP)
  • →HTML-valid — HTL templates are valid HTML files, so designers can open them in a browser
  • →Secure by default — HTL auto-escapes output, preventing XSS attacks
  • →Logic-free by design — complex logic must go in Sling Models or Use-classes
  • →Server-side rendered — runs on the AEM server before sending HTML to the browser

HTL files have the .html extension and live in your component folder alongside the dialog.

🎯 Why Do We Need It?

Before HTL, AEM used JSP (JavaServer Pages). JSP had serious problems:

  • →Security holes — developers could write unescaped output, causing XSS
  • →Mixed concerns — Java logic mixed with HTML made templates unmaintainable
  • →Designer unfriendly — JSP tags broke HTML structure

HTL solved all of this:

  • →Auto-escaping prevents XSS by default
  • →Separation of concerns — logic stays in Java, templates stay in HTML
  • →HTML-valid — designers can open templates in browsers
  • →Simpler syntax — data-sly-* attributes are easier to read than JSP tags

🏗️ What Happens in Real Projects?

In a real project, every component has an HTL file. Here's a typical day:

  • →You create a component folder with hero.html
  • →The HTL file reads data from a Sling Model using data-sly-use
  • →It conditionally renders sections using data-sly-test
  • →It loops over lists using data-sly-list
  • →It includes other components using data-sly-resource
  • →It includes sub-templates using data-sly-template and data-sly-call

HTL is the glue between your Java logic and the HTML that authors see in the browser.

⚙️ How It Works

Core HTL Attributes

AttributePurpose
data-sly-useImport a Sling Model or Use-class
data-sly-testConditional rendering (like if)
data-sly-listLoop over a collection
data-sly-repeatLoop and repeat the element itself
data-sly-textSet text content
data-sly-attributeSet HTML attributes
data-sly-includeInclude another HTL script
data-sly-resourceInclude a child resource/component
data-sly-templateDefine a reusable template
data-sly-callCall a defined template
data-sly-unwrapRemove the wrapper element

Expression Syntax

HTL uses ${...} for expressions. You can access properties, call getters, and apply options:

  • →${model.title} — calls model.getTitle()
  • →${properties.heading} — reads JCR property directly
  • →${item.url @ context='uri'} — output with explicit context
  • →${model.count > 0} — boolean expression

Context Options

HTL auto-escapes based on context. You can override:

  • →@ context='html' — output raw HTML (use carefully!)
  • →@ context='uri' — for URLs
  • →@ context='text' — for plain text (default)
  • →@ context='unsafe' — no escaping (avoid!)

💻 Example

HTL Patterns You'll Use Every Day — Real examples covering the most common HTL patterns in enterprise AEM projects.

html
<!-- Use a Sling Model -->
<sly data-sly-use.model="com.myproject.core.models.CardModel"/>

<!-- Conditional rendering -->
<div class="card" data-sly-test="${model.heading}">

    <!-- Text output (auto-escaped) -->
    <h2 class="card__title">${model.heading}</h2>

    <!-- Conditional block -->
    <sly data-sly-test="${model.description}">
        <p class="card__desc">${model.description}</p>
    </sly>

    <!-- URL output with context -->
    <a href="${model.linkUrl @ context='uri'}"
       class="card__link">
        ${model.linkLabel}
    </a>

</div>

🤔 Common Confusions

❓ What is the difference between data-sly-text and ${...}?

data-sly-text="value" replaces the entire content of the element. ${value} outputs inline within content. Use data-sly-text when you want to replace all child content; use ${} for inline output within text.

❓ Why does my HTML output show escaped entities like &lt;?

HTL auto-escapes HTML for security. If you need to output raw HTML (e.g., from a Rich Text Editor), use @ context='html': ${model.richText @ context='html'}. Only do this with trusted content — never with user input.

❓ What is <sly> and when should I use it?

<sly> is a virtual element — it's processed by HTL but never rendered in the final HTML. Use it when you need a data-sly-* attribute but don't want to add a wrapper element to your markup.

❓ Can I write if/else in HTL?

Not directly. HTL doesn't have an else statement. The workaround is to use two data-sly-test attributes: one for the true case and one for the false case (negate with !). For complex conditionals, move the logic to your Sling Model.

🚨 Common Production Issues

Blank output

Sling Model failed to instantiate. Check the error.log for model instantiation errors.

XSS vulnerability

developer used @ context='unsafe' or @ context='html' with user-controlled content.

data-sly-list not rendering

the list returned null instead of an empty collection. Always return empty lists, never null.

Template not found

HTL file name doesn't match component node name, or there's a selector mismatch.

Encoding issues

special characters in content appear garbled. Usually a UTF-8 encoding issue in the content or the HTL file itself.

✅ Best Practices

Never use @ context='unsafe'

it disables all XSS protection.

Use <sly> for logic-only elements to keep your HTML clean.

Keep HTL templates simple

if you need more than 3 conditions, move logic to a Sling Model.

Use data-sly-template for repeating patterns within the same file.

Always test your HTL with empty/null data

use DefaultInjectionStrategy.OPTIONAL in models.

Use meaningful variable names in data-sly-use and data-sly-list for readable templates.

🏛️ Architect's Note

Senior Architect's Perspective

HTL's intentional simplicity is a feature, not a limitation. In large teams, it prevents junior developers from putting business logic in templates — which is one of the biggest sources of unmaintainable AEM code.

Governance tip: Establish a rule in your team: if a developer needs more than 2 conditions in HTL, they must move the logic to a Sling Model. This keeps templates clean and testable.

For AEM as a Cloud Service: HTL templates are compiled at build time in ACS, which means syntax errors are caught earlier. Use the HTL Maven Plugin in your build to validate HTL before deployment.

💬 Frequently Asked Questions

Is HTL the same as Sightly?

Yes. Sightly was the original name when Adobe introduced it in AEM 6.1. It was renamed to HTL (HTML Template Language) in AEM 6.3 when it became an Apache open-source project. You'll see both names in older projects and documentation.

Can I use JavaScript in HTL?

HTL supports Use-API in JavaScript (Use.js files). You can write a .js file alongside your component and use it with data-sly-use. However, the Java Sling Model approach is preferred for complex logic because it's faster and more testable.

How do I debug HTL templates?

Add ?debug=layout to the URL to see the page structure. For model issues, check error.log. You can also add temporary sly elements with data-sly-test="${true}" to test rendering. The HTL REPL tool in CRXDE is also useful for testing expressions.

Can HTL access the JCR directly without a Sling Model?

Yes. HTL automatically provides: properties (current node's ValueMap), pageProperties (current page properties), inheritedPageProperties, currentNode, currentPage, resource, and request. For simple components, you can use ${properties.heading} directly without a model.

🎯 Key Takeaways

1HTL = HTML + data-sly-* attributes for dynamic content
2Auto-escaping prevents XSS by default — use @ context options carefully
3<sly> renders nothing in HTML — use it for logic-only elements
4data-sly-use loads Sling Models; data-sly-list loops; data-sly-test conditionals
5Keep logic out of HTL — complex logic belongs in Sling Models
6HTL is intentionally simple — that simplicity is a design feature

🎯 Practice this topic in interviews

↗ Where this topic fits

Sources

Sources / References

Use these AEM references to verify the concept in official docs and continue into the most relevant SplashRide pages.

Related SplashRide pages

SplashRide AEM learning path

Browse the full AEM topic collection and learning order on SplashRide.

AEM Interview Prep

Practice AEM interview questions and scenario-based answers.

AEM Developer Career Path

This AEM developer career path explains how to become an AEM developer through JCR, Sling, HTL, client libraries, Dispatcher, Cloud Service delivery, project depth, and interview-ready enterprise implementation skills.

Drupal Developer Career Path

A practical route through Drupal content modeling, site building, theming, modules, configuration management, APIs, performance, deployment discipline, and interview preparation.

AEM Components Explained: Dialogs, HTL, Sling Models, and Core Components

Learn AEM components with component nodes, dialogs, HTL, Sling Models, Core Components, authoring, inheritance, and real project patterns.

Sling Models

Understand Sling Models — the backbone of AEM component logic. Learn how they work, how to write them, and the gotchas that trip up every developer.