Skip to main content

Decoration Tag (cq:htmlTag)

When AEM renders an included editable component and decoration is requested, it can wrap the component's own output in an extra HTML element called the decoration tag. This is the element you see around your markup with a class that matches the component name, for example:

<div class="text aem-GridColumn aem-GridColumn--default--12">
<!-- the component's own HTL output starts here -->
<div class="cmp-text">...</div>
</div>

The outer <div class="text ..."> is not produced by your HTL. AEM's decoration mechanism adds it when a container or include asks for decoration (for example editable child components in the Page Editor), so the authoring UI (drag handles, the responsive grid, in-place editing) has a stable element to hook into, and so layout/grid classes have somewhere to live.

cq:htmlTag is the component node that lets you control what that decoration element looks like - which HTML tag it uses and which attributes (most importantly class) it carries.

Official documentation

Where it lives: node structure​

cq:htmlTag is a child node of your component. In a file/Vault representation it is a _cq_htmlTag folder containing a .content.xml. The properties on that node become attributes on the decoration element.

apps/myproject/components/text/_cq_htmlTag/.content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
jcr:primaryType="nt:unstructured"
cq:tagName="section"
class="my-text-wrapper"/>

The component node itself looks like this, with the _cq_htmlTag node sitting next to the dialog and HTL:

Component node layout
text/
├── .content.xml # cq:Component
├── _cq_dialog/
│ └── .content.xml
├── _cq_htmlTag/ # the decoration tag node
│ └── .content.xml
└── text.html # HTL

Two properties do the heavy lifting:

PropertyEffect
cq:tagNameThe HTML element used for the decoration tag (default: div).
classCSS class(es) added to the decoration element.

Any other property you add is rendered as a literal attribute on the decoration element (for example role="region" or a data-* attribute), which makes cq:htmlTag a convenient place to attach static, component-wide attributes.

Separately, the component node itself can set cq:noDecoration="{Boolean}true" to suppress wrapper generation even when an include requests decoration.

How the class list is assembled​

The class attribute on the decoration element is a merge of several sources:

  1. The component name (added automatically by the layout/grid include).
  2. Responsive grid classes such as aem-GridColumn (when inside a layout container).
  3. The class property from cq:htmlTag.

So a cq:htmlTag with class="my-text-wrapper" does not replace the component name - it is added alongside it. You end up with something like class="text my-text-wrapper aem-GridColumn ...".

You cannot remove the component-name class via cq:htmlTag

The component-name token comes from the include itself, not from cq:htmlTag. To change it you would have to rename the component (breaking existing content references) or suppress decoration entirely (see below). cq:htmlTag only lets you add classes/attributes and change the tag name.

When it is helpful​

Semantic HTML wrappers​

The default div is rarely the most meaningful element. Use cq:tagName to emit landmark or list elements so the rendered page is more semantic and accessible - without polluting your HTL with the wrapper.

A component rendered as a list item
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
jcr:primaryType="nt:unstructured"
cq:tagName="li"
class="nav__item"/>
A component rendered as a <section> landmark
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
jcr:primaryType="nt:unstructured"
cq:tagName="section"
class="promo"
role="region"/>

A stable styling hook​

When your CSS or BEM convention needs a reliable wrapper class on the decoration element (rather than only on the inner markup your HTL controls), cq:htmlTag gives you one place to declare it.

Overriding classes inherited from a supertype​

This is the most common "gotcha" worth knowing. When a component sets sling:resourceSuperType to a core component, it inherits that core component's cq:htmlTag. The Core Components add their own classes this way - for example the Experience Fragment component contributes an experiencefragment class to the decoration element.

If you extend such a component but want a different (or no) extra class, declare a local _cq_htmlTag. A local decoration-tag node shadows the inherited one entirely:

apps/myproject/components/richtext-xf-reference/.content.xml
<?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"
jcr:primaryType="cq:Component"
jcr:title="Richtext XF Reference"
sling:resourceSuperType="core/wcm/components/experiencefragment/v2/experiencefragment"/>
apps/myproject/components/richtext-xf-reference/_cq_htmlTag/.content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="nt:unstructured"
class="richtext"/>

Result: the inherited experiencefragment class is gone and richtext is used instead, so the wrapper lines up with a plain richtext component's wrapper for styling purposes:

<!-- before: inherited class -->
<div class="richtext-xf-reference experiencefragment">...</div>

<!-- after: local _cq_htmlTag with class="richtext" -->
<div class="richtext-xf-reference richtext">...</div>

Disabling or overriding decoration at include time​

cq:htmlTag configures decoration per component. You can also control it per include from HTL, which is useful when the same component should be wrapped differently in different contexts (or not at all).

Override the tag name and class for a single include
<sly data-sly-resource="${ 'content' @
resourceType='myproject/components/text',
decorationTagName='article',
cssClassName='featured' }"></sly>
Code-reuse include: no decoration element is rendered
<sly data-sly-resource="${ 'content' @
resourceType='myproject/components/text' }"></sly>

A plain HTL include like this one renders the child's markup without a wrapper. To guarantee that a component is never decorated, even when a container or an include asks for decoration with decoration=true, set cq:noDecoration on the component definition instead:

ui.apps/.../components/text/.content.xml
<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
xmlns:cq="http://www.day.com/jcr/cq/1.0"
jcr:primaryType="cq:Component"
jcr:title="Text"
componentGroup="My Project - Content"
cq:noDecoration="{Boolean}true"/>
warning

Without a decoration element, the Page Editor has no wrapper to attach its overlay to, so a cq:noDecoration component cannot be selected or edited on its own. Use it only for components that are rendered for code reuse, not for components authors drop into a layout container.

HTL includes do not decorate by default

Plain HTL code-reuse includes such as data-sly-resource="child" do not render a wrapper by default. Use decoration=true, decorationTagName, or the Page Editor's container includes when the child must be editable or needs layout classes. Empty decoration tags are also suppressed automatically when the component renders no output.

Gotchas​

  • It adds, it does not replace. class on cq:htmlTag is merged with the component name and grid classes.
  • Inheritance shadows, it does not merge. A local _cq_htmlTag replaces the inherited one wholesale; you do not get a union of inherited and local properties.
  • cssClassName vs cq:htmlTag. The cssClassName include option and the design-dialog Style System add classes at a different layer than cq:htmlTag; reach for cq:htmlTag for static, code-owned defaults and the Style System for author-selectable styles.
  • Empty wrappers disappear. If the component produces no markup, AEM drops the decoration element, so do not rely on it always being present in the DOM.

References​