Building reusable Sightly components with HTL template inheritance

A reusable Adobe Experience Manager component should separate agreed visual structure from content that varies by page. Sightly, also known as HTL, gives front-end developers a declarative way to express that contract while AEM resolves resources, models and server-side rendering. The result can be maintained by platform engineers without turning each project into a collection of copied fragments.

Template inheritance adds a controlled extension point: a child template can preserve a parent’s structure, then replace or add only what a project needs. This is useful when the same card, teaser or navigation pattern appears across Australian retail, banking and government sites, yet each brand needs small differences in imagery, calls to action and accessibility wording.

The key is to treat inheritance as an API rather than a convenient way to move markup around. Clear model inputs, stable class names, documented context and focused tests let teams consume the component confidently, while designers and content authors retain the flexibility needed for local market requirements.

Inheritance as a component contract

Start by naming what every implementation must guarantee. For a promotional card, that might include a heading, media region, optional body copy and a valid link. The base HTL template owns the semantic order and accessible fallback; specialised templates should extend those guarantees instead of redesigning the entire pattern. This makes the component predictable for CSS, client libraries and automated checks.

AEM’s resource hierarchy can support HTL template inheritance. A shared component can point to a base implementation through sling:resourceSuperType, while project resources override selected behaviour. Within HTL, data-sly-resource composes resources, while data-sly-super delegates rendering to a supertemplate implementation. The mechanism may vary between projects, but the ownership boundary should remain explicit.

The archived ICF Olson speaker profile reflects CIRCUIT’s Chicago focus on the people and architectural decisions behind AEM delivery.

Defining reusable component boundaries

A component becomes reusable when its input is explicit. Keep display data in Sling Model objects, use context to control scope, and avoid reaching into unrelated nodes from the template. A CardModel can expose heading, image, link label, link URL and variants, giving HTL stable fields even when the underlying JCR structure changes.

Class names are part of the public contract. Establish predictable naming, document modifier classes, and keep IDs for behaviour rather than styling. If context changes the HTML, state that rule in the contract and test each supported context. A global header may look reusable but fail when embedded in a page that already supplies a landmark or heading level.

For teams in Sydney and Melbourne supporting national campaigns, this matters when one component must work on a corporate site, a campaign landing page and a customer portal. Australian English labels such as organisation, authorised and centre belong in the content layer; structural classes and DOM order should remain product-agnostic.

Composing hierarchy with HTL

Use data-sly-resource to define where child resources appear, rather than embedding one template’s internals inside another. A base article template might expose heading, media and body slots. A brand variant can supply specialised resources while retaining the same outer article and landmark structure, giving authors predictable editing controls.

The following fragment separates presentation from composition. The referenced media resource owns its fallback behaviour, so the card does not need to know whether the image comes from DAM, an asset reference or a remote source.

<article class="card" data-sly-attribute.id="${card.id}">
  <div data-sly-resource="${'media' @ resourceType='acme/components/media'}">
    <img data-sly-test.src="${card.image.src}"
         src="${card.image.src}" alt="${card.image.alt}" />
  </div>
  <div data-sly-resource="${'body' @ resourceType='acme/components/card-body'}">
    <h3 data-sly-test.text="${card.title}">${card.title}</h3>
    <p data-sly-test.text="${card.summary}">${card.summary}</p>
  </div>
</article>

Composition and inheritance solve different problems: one controls placement and ownership, while the other defines how implementations refine a shared template. Keep both explicit. A child may override the body resource without changing the card shell, and the base implementation can still be tested independently.

Comparing composition approaches

Different projects need different balances of control and flexibility. The choice should follow the volatility of the design, the number of consuming teams and the degree to which content authors need independent control.

A small pilot can reveal where markup has become coupled to project-specific assumptions. Review the component in its base form and in one branded implementation before promoting it to a shared library.

Approach Strongest use Inheritance behaviour Main cost
HTL parent and child templates A stable visual shell with small branded variations The child extends the parent and replaces selected regions Parent changes can affect every child
Resource composition with data-sly-resource Independently authored or reusable content regions Each resource retains its own implementation and hierarchy More resource boundaries and context rules
Shared Sling Model with one HTL template Components whose inputs vary more than their markup Logic and fallback behaviour come from shared code Large conditional templates can become difficult to read
Project resource with a reusable base Gradual adoption across brands and sites A supertype supplies defaults while a resource selects an override Migration and compatibility require active governance

Whichever pattern wins, publish one example and one failure case. Distributed teams in Perth, Brisbane and Adelaide can then review the same contract despite different time zones, and an approval can be recorded without another workshop.

Context, attributes, and safe reuse

Context is where generic components often become unpredictable. Use explicit context values to select behaviour, and keep context names aligned with the component’s role. data-sly-attribute is useful for controlled modifiers, but data-sly-test should guard assumptions rather than conceal missing data. If a card has no destination, it should render as a non-interactive article, not an anchor without href.

Accessibility checks belong in the same review as visual testing. Confirm heading order, alternative text, keyboard focus and link purpose for every inherited variant. A base template may provide an empty alt attribute for decorative media, but meaningful images need context-sensitive alternative text supplied by the model.

At system boundaries, reusable markup can expose events or data without becoming dependent on a particular cloud. The CIRCUIT post on hybrid cloud event routing illustrates the same separation: components publish stable contracts, while Azure Service Bus or another broker handles transport.

Testing inherited implementations

Testing should cover both template layers. Unit tests for Sling Models verify mapping and fallback logic; HTL rendering tests confirm attributes, conditional blocks and resource references. A visual comparison can catch hierarchy regressions, but it cannot prove that optional values degrade safely when content is incomplete.

Keep a fixture matrix for base, inherited and overridden resources, including absent media, long Australian place names and translated labels. Character-based tests should also consider content from Adelaide, Darwin and regional offices rather than relying only on short English samples.

Access control deserves an equally explicit contract. This review of Blazor authorization policies shows how policy-based rules can be tested independently of the view; similar separation helps AEM teams decide which component variants are available to authors and which remain restricted.

Operating a shared component library

Promotion into a shared package requires governance. Store the base template and models under an agreed namespace, publish compatibility notes, and make breaking changes visible. Versioning can use package conventions or a controlled release branch, but the contract should include supported AEM versions, client-library dependencies and naming guarantees.

Localisation also needs ownership. Australian teams should agree whether date, currency and number formats come from the AEM locale, an authoring dialog or the consuming application. Australian Privacy Principles may require consent labels and disclosure behaviour to vary by campaign, and those choices are safer in models than in each template variant.

Design reviews should include front-end, AEM, content and accessibility representatives. That combination catches semantic changes early and prevents a component from becoming a technical dependency for one team. The archive of the CIRCUIT developer conference supports shared learning through its AEM session recordings from 2015 and 2016.


The practical route is to begin with a duplicated front end that has already caused visual drift. Extract the stable markup into a base HTL template, define its Sling Model contract, and create one deliberately specialised child implementation. This small proof reveals whether inheritance reduces maintenance without hiding authoring complexity.

Create a component repository entry, add rendering and accessibility tests, and ask the owning teams to validate the first inherited variant against real Australian campaign content. When the contract holds, publish the base template, examples and migration notes, then schedule a reuse review with your AEM and front-end teams.