HTL Tutorial #1: Introduction to HTML Template Language

HTL Tutorial #1: Introduction to HTML Template Language

What is HTL?

HTL (HTML Template Language), previously called Sightly, is the recommended and preferred templating system for Adobe Experience Manager (AEM).

Why HTL?

  1. Built-in security: Automatic protection against XSS (Cross-Site Scripting)
  2. Simplicity: HTML-like syntax, easy to read and write
  3. Logic/presentation separation: Complex logic goes in Java/JS, not in the template
  4. Performance: Compiled into Java Servlets for efficient server-side execution
  5. Designer-friendly: Designers can work with HTML without knowing Java

Basic Syntax

HTL uses two main constructs:

1. Expressions ${...}

HTL expressions are enclosed between ${ and }:

<h1>${properties.title}</h1>

Output:

<h1>Welcome to my site</h1>

2. Attributes data-sly-*

HTL attributes start with data-sly- and control element behavior:

<div data-sly-test="${properties.showContent}">
  <p>This content is shown only if showContent is true</p>
</div>

First Complete Example

Let's create a simple HTL component to display author information:

<!--/* Simple Author Component */-->
<div class="author-card">
  <!-- Title with expression -->
  <h2>${properties.authorName}</h2>

  <!-- Conditional paragraph -->
  <p data-sly-test="${properties.bio}">${properties.bio}</p>

  <!-- Link only if present -->
  <a data-sly-test="${properties.website}"
     href="${properties.website}">
    Visit the site
  </a>
</div>

Component data (dialog)

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:sling="http://sling.apache.org/jcr/sling/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"
          xmlns:granite="http://www.adobe.com/jcr/granite/1.0"
    jcr:primaryType="nt:unstructured"
    jcr:title="Proprietà Autore"
    sling:resourceType="cq/gui/components/authoring/dialog">
    
    <content
        jcr:primaryType="nt:unstructured"
        sling:resourceType="granite/ui/components/coral/foundation/container">
        
        <layout
            jcr:primaryType="nt:unstructured"
            sling:resourceType="granite/ui/components/coral/foundation/tabs"
            type="nav"/>
        
        <items jcr:primaryType="nt:unstructured">
            
            <properties
                jcr:primaryType="nt:unstructured"
                jcr:title="Informazioni Autore"
                sling:resourceType="granite/ui/components/coral/foundation/container">
                
                <layout
                    jcr:primaryType="nt:unstructured"
                    sling:resourceType="granite/ui/components/coral/foundation/layouts/fixedcolumn"
                    margin="{Boolean}false"/>
                
                <items jcr:primaryType="nt:unstructured">
                    
                    <column
                        jcr:primaryType="nt:unstructured"
                        sling:resourceType="granite/ui/components/coral/foundation/container">
                        
                        <items jcr:primaryType="nt:unstructured">
                            
                            <authorName
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                fieldLabel="Nome Autore"
                                name="./authorName"
                                required="{Boolean}true"/>
                            
                            <bio
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textarea"
                                fieldLabel="Biografia"
                                name="./bio"/>
                            
                            <website
                                jcr:primaryType="nt:unstructured"
                                sling:resourceType="granite/ui/components/coral/foundation/form/textfield"
                                fieldLabel="Sito Web"
                                name="./website"
                                validation="url"/>
                                
                        </items>
                    </column>
                </items>
            </properties>
            
            </items>
    </content>
</jcr:root>

HTML Output

With the data:

<div class="author-card">
  <h2>John Smith</h2>
  <p>Senior AEM Developer</p>
  <a href="https://johnsmith.com">Visit the site</a>
</div>

HTL Comments

HTL has a special type of comment that is completely removed from the output:

<!--/* This is an HTL comment */-->
<!--/*
  Multi-line comment
  Will not appear in the final HTML
*/-->

Difference with normal HTML comments:

<!-- This appears in the HTML output -->
<!--/* This is completely removed */-->

Comparison with JSP

JSP (old way):

<h1><%= properties.get("title", "") %></h1>
<% if (properties.get("showContent", false)) { %>
  <p>Content</p>
<% } %>

HTL (new way):

<h1>${properties.title}</h1>
<p data-sly-test="${properties.showContent}">Content</p>

HTL is cleaner, safer, and more readable!

Basic Best Practices

  1. Always use expressions for output: ${...}
  2. Don't put complex logic in the template: use data-sly-use to load Java models
  3. Leverage automatic security: HTL automatically escapes output
  4. Comment your code: use <!--/* */--> to document templates

Next Lesson

In the next lesson we will dive deeper into HTL expressions, data types, and available operators.

Resources


This is lesson #1 of a complete series on HTL. Continue with the next lesson to dive deeper into expressions and syntax.