You are reading the Adobe Experience Manager 6.0 version of Sightly.

This section introduces the Sightly HTML templating system and provides a quick reference of the syntax and available block statements.

What is Sightly?

Sightly is an HTML templating language, introduced with AEM 6.0. It takes the place of JSP (Java Server Pages) and ESP (ECMAScript Server Pages) as the preferred templating system for HTML. The name “Sightly” (meaning “pleasing to the eye”) highlights it’s focus on keeping your markup beautiful, and thus maintainable, once made dynamic.

As in all HTML server-side templating systems, a Sightly template defines the output sent to the browser by specifying the HTML itself, some basic presentation logic and variables be evaluated at runtime.

But, Sightly differs from other templating systems in three main ways:

  • Security by Default: Sightly automatically filters and escapes all variables being output to the presentation layer to prevent cross-site-scripting (XSS) vulnerabilties. As Sightly understands the HTML syntax, it is capable to automatically detect the scope in which variables are placed, and automatically do proper context-aware escaping and XSS protection. Yet, it is possible to manually control the display context if needed.
  • Separation of Concerns: The expressiveness of the Sightly template language is purposely limited, in order to make sure that a real programming language is used to express the corresponding presentation logic. This optional logic is invoked from the template with the Use-API pattern, making it easy to understand what is called for a given view, and to potentially have different logic for different views of the same resource.
  • Sightly is HTML5: A Sightly template is itself a valid HTML5 file. All Sightly-specific syntax is expressed either within a data attribute, or within HTML text. Any Sightly file opened as HTML in an editor will automatically benefit from any features provided by that editor for regular HTML.

Sightly aims to reduce the time to market and the total cost of ownership for AEM projects:

  • Reducing project costs
    by making components editable for front-end developers.
  • Reducing operational costs
    by securing the templates against XSS injections.
  • Reducing maintenance costs
    by keeping templates readable and valid HTML5.


Before diving into the details, here are a few useful related links, either to get started with a more example-oriented methodology, or to complement what is covered on this page.




Code Samples

  • Sightly Page Components Example (running instance required on localhost:4502)
    The Activities section of Geometrixx Outdoors is fully implemented using Sightly and server-side JavaScript for the logic. Enter developer mode and inspect the components on the page to see how they are built.
  • Sightly TodoMVC Example
    A feature-complete implementation of the famous TodoMVC exercise. In just about 300 lines of code, this sample shows how to build web applications with Sightly, getting status persisted on the server using the Apache Sling REST framework.

And follow Sightly on Twitter to keep posted about what's going on in the world of Sightly.

Moving to Sightly

It is recommended for new AEM projects to use Sightly as it offers multiple benefits compared to JSP. For existing projects though, a migration only makes sense if it is estimated to be less effort than maintaining the existing JSPs for the coming years.

But moving to Sightly is not necessarily an all-or-nothing choice, because components written in Sightly are compatible with components written in JSP or ESP. Meaning that existing projects can without a problem use Sightly for new components, while keeping JSP for existing components.

Even within the same component, Sightly templates can be used alongside JSPs and ESPs. For example, a JSP can include a Sightly template like this,

<cq:include script="footer.html"/>

and a Sightly template can include a JSP like this,

<div data-sly-include="footer.jsp"></div>

Getting Started

Every Sightly template is an HTML5 document or fragment, augmented with a specific syntax that adds the dynamic functionality. Here's a first example:

<h1 data-sly-test="${properties.jcr:title}">

Two different kind of syntaxes have to be distinguished:

  • Sightly Block Statements: To define structural block elements within the template, Sightly employs HTML5 data attributes. This allows to attach behavior to existing HTML elements. All Sightly-specific attributes are prefixed with data-sly-.
  • Sightly Expression Language: Sightly expressions are delimited by characters ${ and }. At runtime, these expressions are evauated and their value is injected into the outgoing HTML stream. They can occur within the HTML text or within HTML attribute values.
<p data-sly-use.logic="logic.js">
    <a href="${}">
<h1 data-sly-test="${currentPage.title}">
    <a href="${currentPage.path}.html">

Some additional comments to understand the example from above in detail:

  • Line 1: The data-sly-test statement checks if the page title exists and is not empty. If so, the <h1> element and its content is displayed, otherwise it is removed altogether.
  • Line 2: This expression renders the page path into the href attribute. Since Sightly knows the HTML syntax, href and src attributes are automatically protected against cross-site scripting (XSS) injections accordingly. For instance if the variable contained a javascript: URL, it would have been removed.
  • Line 3: This expression displays the page title as text. Many special characters are escaped to make it impossible to inject cross-site scripts.

The AEM implementation of Sightly is doing all the rendering work on the server, so from the example above, the following HTML result would be sent to the client:

    <a href="/content/my%20page.html">
        My page title&#x21;

Notice how the whitespace in the link has been URL encoded, while the exclamation mark in the content has been HTML encoded. Because these characters can be instrumental in XSS attacks, Sightly automatically escapes them.


Sightly comments are HTML comments with additional syntax. They are delimited like this:

<!--/* A Sightly Comment */-->

Anything within a Sightly comment will be entirely ignored by the Sightly processor and removed from the output.

However, the content of standard HTML comments, delimited like this:

<!-- An HTML Comment -->

will be passed through the Sightly processor and expressions within the comment will be evaluated. But it is not possible to have structural block elements within comments because HTML comments are pure text, even if they contain something that looks like HTML elements.


So, if we have following variable in our context:

obj = {
    foo: "Foo.",
    bar: "Bar."

then this template:

<!--/* The value of foo is: ${} */-->
    The value of bar is: ${}
    <span data-sly-test="${obj.foobar}">${} ${}</span>

will output this HTML:

    The value of bar is: Bar.
    <span data-sly-test="">Foo. Bar.</span>


Sightly expressions are used to access the data structures that provide the dynamic elements of the HTML output. A Sightly expression is delimited by ${ and }. The expression syntax includes literals, variables, operators and options:



Boolean represents a logical entity and can have two values: true, and false.

${true} ${false}


There is only one number type: integers. Floating point numbers are not supported:



They represent textual data, and can be single or double quoted:

${'foo'} ${"bar"}

Single character escape  sequences: \\ \' \" \t \n \r \f \b

${'it\'s great'}

Unicode escape sequences: \u followed by 4 hexadecimal digits:

${'it\u0027s great'}

Some useful escape codes:

  • \u0022 for "
  • \u0027 for '
  • \u003c for <
  • \u003e for >


Simple member access:


Colons are permitted in identifiers, which is useful for accessing JCR properties:


Accessing members with special characters (in this case a space):

${properties['my property']}

Accessing members dynamically:



Logical NOT

Logical NOT:


Logical AND

Logical AND:

${varOne && varTwo}

Logical OR (can be used to provide string defaults):

${varOne || varTwo}
${properties.text || properties.default}


${varChoice ? varOne : varTwo}

Comparison (only supports integers):

${varOne < varTwo}
${varOne > varTwo}
${varOne <= varTwo}
${varOne >= varTwo}

Equality and inequality (only supports two variables of identical type):

${varOne == varTwo}
${varOne != varTwo}

Grouping parentheses:

${varOne && (varTwo || varThree)}	


Expression options can act on the expression and modify it, or serve as parameters when used in conjunction with block statements.

Everything after the '@' is an option:

${myVar @ optOne}

Options can have a value, which may be a variable or a literal:

${myVar @ optOne=bar}
${myVar @ optOne='bar'}

Multiple options are separated by commas:

${myVar @ optOne, optTwo=bar}

Parametric expressions containing only options are also possible:

${@ optOne, optTwo=bar}

Block Statements

Sightly block statements are custom data attributes added directly to existing HTML. This allows easy and unobtrusive annotation of a prototype static HTML page, converting it to a functioning dynamic template without breaking the validity of the HTML code.


data-sly-use: Initializes a helper object (defined in JavaScript or Java) and exposes it through a variable.

Initialize a JavaScript object, where the source file is located in the same directory as the template. Note that the filename must be used:

<div data-sly-use.nav="navigation.js">${}</div>

Initialize a Java class, where the source file is located in the same directory as the template. Note that the classname must be used, not the file name:

        <div data-sly-use.nav="Navigation">${}</div>

Initialize a Java class, where that class is installed as part of an OSGi bundle. Note that its fully-qualified class name must be used:

        <div data-sly-use.nav="org.example.Navigation">${}</div>

Parameters can be passed to the initialization using options. Generally this feature should only be used by Sightly code that is itself within a data-sly-template block:

<div data-sly-use.nav="${'navigation.js' @parentPage=currentPage}">${}</div>

Initialize another Sightly template that can then be called using data-sly-call:

<div data-sly-use.nav="navTemplate.html" data-sly-call="${}"></div>


For more information on the Use-API, see:


data-sly-unwrap: Removes the host element from the generated markup while retaining its content. This allows the exclusion of elements that are required as part of Sightly presentation logic but are not desired in the actual output.

However, this statement should be used sparingly. In general it is better to keep the Sightly markup as close as possible to the intended output markup. In other words, when adding Sightly block statements, try as much as possible to simply annotate the existing HTML, without introducing new elements.

For example, this

<p data-sly-use.nav="navigation.js">Hello World</p>

will produce

<p>Hello World</p>

Whereas this,

<p data-sly-use.nav="navigation.js" data-sly-unwrap>Hello World</p>

will produce

Hello World

It is also possible to conditionally unwrap an element:

<div class="popup" data-sly-unwrap="${isPopup}">content</div>


data-sly-text: Replaces the content of its host element with the specified text.

For example, this


is equivalent to

<p data-sly-text="${properties.jcr:description}">Lorem ipsum</p>

Both will display the value of jcr:description as paragraph text. The advantage of the second method is that is allows the unobtrusive annotation of HTML while keeping the static placeholder content from the original designer.


data-sly-attribute: Adds attributes to the host element.

For example, this

<div title="${properties.jcr:title}"></div>

is equivalent to

<div title="Lorem Ipsum" data-sly-attribute.title="${properties.jcr:title}"></div>

Both will set the title attribute to the value of jcr:title. The advantage of the second method is that is allows the unobtrusive annotation of HTML while keeping the static placeholder content from the original designer.

Attributes are resolved left to right, with the rightmost instance of an attribute (either literal or defined via data-sly-attribute) taking precedence over any instances of the same attribute (defined either literally or via data-sly-attribute) defined to its left.

Note that an attribute (either literal or set via data-sly-attribute) whose value evaluates to the empty string will be removed in the final markup. The one exception to this rule is that a literal attribute set to a literal empty string will be preserved. For example,

<div class="${''}""${''}"></div>




<div class=""""></div>


<div class=""></div>

To prevent the stripping of empty attributes, set their value to an array containing an empty string. For example,

<div class="${['']}""${['']}"></div>


<div class="" id=""></div>

To set multiple attributes, pass a map object hold key-value pairs corresponing to the attributes and their values. For example, assuming,

attrMap = {
    title: "myTitle",
    class: "myClass",
    id: "myId"


<div data-sly-attribute="${attrMap}"></div>


<div title="myTitle" class="myClass" id="myId"></div>


data-sly-element: Replaces the element name of the host element.

For example,

<h1 data-sly-element="${titleLevel}">text</h1>

Replaces the h1 with the value of titleLevel.

For security reasons, data-sly-element accepts only the following element names:

a abbr address article aside b bdi bdo blockquote br caption cite code col colgroup
data dd del dfn div dl dt em figcaption figure footer h1 h2 h3 h4 h5 h6 header i ins
kbd li main mark nav ol p pre q rp rt ruby s samp section small span strong sub 
sup table tbody td tfoot th thead time tr u var wbr

To set other elements, XSS security must be turned off (@context='unsafe').


data-sly-test: Conditionally removes the host element and it's content. A value of false removes the element; a value of true retains the element.

For example, the p element and its content will only be rendered if isShown is true:

<p data-sly-test="${isShown}">text</p>

The result of a test can be assigned to a variable that can be used later. This is usually used to construct "if else" logic, since there is no explicit else statetment:

<p"${a || b || c}">is true</p>
<p data-sly-test="${!abc}">or not</p>

The variable, once set, has global scope within the Sightly file.


data-sly-list: Repeats the content of the host element for each enumerable property in the provided object.

Here is a simple loop:

<dl data-sly-list="${currentPage.listChildren}">
    <dt>index: ${itemList.index}</dt>
    <dd>value: ${item.title}</dd>

The following default variables are available within the scope of the list:

item: The current item in the iteration.

itemList: Object holding the following properties:

index: zero-based counter (0..length-1).

count: one-based counter (1..length).

first: true if the current item is the first item.

middle: true if the current item is neither the first nor the last item.

last: true if the current item is the last item.

odd: true if index is odd.

even: true if index is even.

Defining an identifier on the data-sly-list statement allows you to rename the itemList and item variables. item will become <variable> and itemList will become <variable>List.

<dl data-sly-list.child="${currentPage.listChildren}">
    <dt>index: ${childList.index}</dt>
    <dd>value: ${child.title}</dd>

You can also access properties dynamically:

<dl data-sly-list.child="${myObj}">
    <dt>key: ${child}</dt>
    <dd>value: ${myObj[child]}</dd>


data-sly-resource: Includes the result of rendering the indicated resource through the sling resolution and rendering process.

A simple resource include:

<article data-sly-resource="path/to/resource"></article>


Options allow a number of addtional variants:

Manipulating the path of the resource:

<article data-sly-resource="${ @ path='path/to/resource'}"></article>
<article data-sly-resource="${'resource' @ prependPath='my/path'}"></article>
<article data-sly-resource="${'my/path' @ appendPath='resource'}"></article>

Add (or replace) a selector:

<article data-sly-resource="${'path/to/resource' @ selectors='selector'}"></article>

Add, replace or remove multiple selectors:

<article data-sly-resource="${'path/to/resource' @ selectors=['s1', 's2']}"></article>

Add a selector to the existing ones:

<article data-sly-resource="${'path/to/resource' @ addSelectors='selector'}"></article>

Remove some selectors from the existing ones:

<article data-sly-resource="${'path/to/resource' @ removeSelectors='selector1'}"></article>

Remove all selectors:

<article data-sly-resource="${'path/to/resource' @ removeSelectors}"></article>

Overrides the resource type of the resource:

<article data-sly-resource="${'path/to/resource' @ resourceType='my/resource/type'}"></article>

Changes the WCM mode:

<article data-sly-resource="${'path/to/resource' @ wcmmode='disabled'}"></article>

By default, the AEM decoration tags are disabled, the decorationTagName option allows to bring them back, and the cssClassName to add classes to that element.

<article data-sly-resource="${'path/to/resource' @ decorationTagName='span',


data-sly-include: Replaces the host element with the markup generated by the indicated HTML template file (Sightly, JSP, ESP etc.) when it is procesed by its corresponding template engine. The rendering context of the included file will not include the current Sightly context (that of the including file); Consequently, for inclusion of Sightly files, the current data-sly-use would have to be repeated in the included file (In such a case it is usually better to use data-sly-template and data-sly-call)

A simple include:

<section data-sly-include="path/to/template.html"></section>

JSPs can be included the same way:

<section data-sly-include="path/to/template.jsp"></section>

Options let you manipulate the path of the file:

<section data-sly-include="${ @ path='path/to/template.html'}"></section>
<section data-sly-include="${'template.html' @ prependPath='my/path'}"></section>
<section data-sly-include="${'my/path' @ appendPath='template.html'}"></section>

You can also change the WCM mode:

<section data-sly-include="${'template.html' @ wcmmode='disabled'}"></section>

template & call

data-sly-template: Defines a template. The host element and its content are not output by Sightly

data-sly-call: Calls a template defined with data-sly-template. The content of the called template (optionally parameterized) replaces the host element of the call.

Define a static template and then call it:

<div data-sly-call="${one}"></div>

Define a dynamic template and then call it with parameters:

<template data-sly-template.two="${ @ title}"><h1>${title}</h1></template>
<div data-sly-call="${two @ title=properties.jcr:title}"></div>

Templates located in a different file, can be initialised with data-sly-use. Note that in this case data-sly-use and data-sly-call could also be placed on the same element:

<div data-sly-use.lib="templateLib.html">
    <div data-sly-call="${}"></div>
    <div data-sly-call="${lib.two @ title=properties.jcr:title}"></div>

Template recursion is supported:

<template data-sly-template.nav="${ @ page}">
    <ul data-sly-list="${page.listChildren}">
            <div class="title">${item.title}</div>
            <div data-sly-call="${nav @ page=item}" data-sly-unwrap></div>
<div data-sly-call="${nav @ page=currentPage}" data-sly-unwrap></div>

Expression Options

String Formatting

Replaces the enumerated placeholders, {n}, with the corresponding variable:

${'Page {0} of {1}' @ format=[current, total]}


Translates the string to the language of the current source (see below), using the current dictionary. If no translation is found, the original string is used.

${'Page' @ i18n}

The hint option can be used to provide a comment for translators, specifying the context in which the text is used:

${'Page' @ i18n, hint='Translation Hint'}

The default source for the language is 'resource', meaning that the text gets translated to the same language as the content. This can be changed to 'user', meaning that the language is taken from the browser locale or from the locale of the logged-in user:

${'Page' @ i18n, source='user'}

Providing an explicit locale overrides the source settings:

${'Page' @ i18n, locale='en-US'}

To embed variables into a translated string, the format option can be used:

${'Page {0} of {1}' @ i18n, format=[current, total]}

Array Join

By default, when displaying an array as text, Sightly will display comma separated values (without spacing).

Use the join option to specify a different separator:

${['one', 'two'] @ join='; '}

Display Context

The display context of a Sightly expression refers to its location within the structure of the HTML page. For example, if the expression appears in place that would produce a text node once rendered, then it is said to be in a text context. If it is found within the value of an attribute, then it is said to be in an attribute context, and so forth.

With the exception of script (JS) and style (CSS) contexts, Sightly will automatically detect the context of expressions and escape them appropriately, to prevent XSS security problems. In the case of scripts and CSS, the desired context behavior must be explicitly set. Additionally, the context bahavior can also be explicitly set in any other case where an override of the automatic beahvior is desired.

Here we have three variables in three different contexts: (uri context), properties.title (attribute context) and properties.text (text context). Sightly will escape each of these differently in accordance with the security requirements of their respective contexts. No explicit context setting is required in normal cases such as this one:

<a href="${}" title="${properties.title}">${properties.text}</a>

To safely output markup (that is, where the expression itself evaluates to HTML), the html context is used:

<div>${properties.richText @ context='html'}</div>

Explicit context must be set for style contexts:

<span style="color: ${properties.color @ context='styleToken'};">...</span>

Explicit context must be set for script contexts:

<span onclick="${properties.function @ context='scriptToken'}();">...</span>

Escaping and XSS protection can also be turned off:

<div>${myScript @ context='unsafe'}</div>

Context Settings

Context When to use What it does
text Default for content inside elements Encodes all HTML special characters.
html To safely output markup Filters HTML to meet the AntiSamy policy rules,
removing what doesn't match the rules.
attribute Default for attribute values Encodes all HTML special characters.
uri To display links and paths
Default for href and src attribute values
Validates URI for writing as an href or src attribute value,
outputs nothing if validation fails.
number To display numbers Validates URI for containing an integer,
outputs zero if validation fails.
attributeName Default for data-sly-attribute when setting attribute names Validates the attribute name,
outputs nothing if validation fails.
elementName Default for data-sly-element Validates the element name,
outputs nothing if validation fails.
scriptToken For JS identifiers, literal numbers, or literal strings Validates the JavaScript token,
outputs nothing if validation fails.
scriptString Within JS strings Encodes characters that would break out of the string.
scriptComment Within JS comments Validates the JavaScript comment,
outputs nothing if validation fails.
styleToken For CSS identifiers, numbers, dimensions, strings, hex colours or functions. Validates the CSS token,
outputs nothing if validation fails.
styleString Within CSS strings Encodes characters that would break out of the string.
styleComment Within CSS comments Validates the CSS comment,
outputs nothing if validation fails.
unsafe Only if none of the above does the job Disables escaping and XSS protection completely.

AEM Helpers

Client Libraries

Prior to Sightly, client libraries were loaded on a page using a JSP taglib. However, JSP taglibs cannot be used in Sightly scripts. Therefore, some helper templates provide this functionality.

The client libraries helper template library (/libs/granite/sightly/templates/clientlib.html) can be loaded through data-sly-use and stored in a clientLib block element variable. Loading the library's CSS style sheets and JavaScript is done through data-sly-call. The clientLib template library exposes three templates:

  • css - loads only the CSS files of the referenced client libraries
  • js - loads only the JavaScript files of the referenced client libraries
  • all - loads all the files of the referenced client libraries

Each helper template defines a categories option that accepts either an array of string values or a string containing a comma separated values list for referencing the desired client libraries.

Here are a few short examples:

Referencing client libraries components in different sections of a page
<head data-sly-use.clientLib="${'/libs/granite/sightly/templates/clientlib.html'}">
    <css data-sly-call="${clientLib.css @ categories=['category1', 'category2']}" data-sly-unwrap/>
    <!-- content -->
    <js data-sly-call="${clientLib.js @ categories=['category1', 'category2']}" data-sly-unwrap/>
Referencing client libraries in the <head> tag of a page
<head data-sly-use.clientLib="${'/libs/granite/sightly/templates/clientlib.html'}">
    <clientlib data-sly-call="${clientLib.all @ categories=['category1', 'category2']}" data-sly-unwrap/>