Component Basics component-basics

In this chapter, let’s explore the underlying technology of an Adobe Experience Manager (AEM) Sites Component through a simple HelloWorld example. Small modifications are made to an existing component, covering topics of authoring, HTL, Sling Models, Client-side libraries.

Prerequisites prerequisites

Review the required tooling and instructions for setting up a local development environment.

The IDE used in the videos is Visual Studio Code and the VSCode AEM Sync plugin.

Objective objective

  1. Learn the role of HTL templates and Sling Models to dynamically render HTML.
  2. Understand how Dialogs are used to facilitate authoring of content.
  3. Learn the very basics of Client-side libraries to include CSS and JavaScript to support a component.

What you are going to build what-build

In this chapter, you perform several modifications to a simple HelloWorld component. While making updates to the HelloWorld component, you learn about the key areas of AEM component development.

Chapter Starter Project starter-project

This chapter builds upon a generic project generated by the AEM Project Archetype. Watch the below video and review the prerequisites to get started!

If you successfully completed the previous chapter, you can reuse the project and skip the steps for checking out the starter project.
In this step, I’m going to check out the starter project for this chapter from the AEM guides WKND Git Repository. Note that if you have a working AEM project that you finished building in the previous chapter on project set up, feel free to continue using that same project. In my case, I’ll check out the starter code for this chapter directly from Git using command line terminal.
I’ll open up a new terminal and to keep things clean I’ll create a new directory.
Next, I’ll clone the WKND GitHub repository using Git clone and I’ll add the tutorial /component basics -start branch dash as an additional parameter. This allows me to clone and immediately check out that branch with a single command. Now that the repo has been cloned, I can go ahead and build and deploy the project to a local instance of AEM using maven. I’ll use the profile auto install single package to build the entire project and deploy it as a single package. The chapter starter project has been modified slightly to be backwards compatible with AEM 6.5 and 6.4. So if you’re following this tutorial using AEM 6.5 or 6.4, you will need to add an additional profile flag classic.
In this video, my local instance of AM is using the cloud ready Quickstart SDK so I don’t need this classic parameter.
I’ll go ahead and execute the Maven command.
Now I’ve gone ahead and sped up the video, but it typically takes around a minute to finish the build. You should see this build success message.
Next, I can quickly log into my AEM environment and under deployment, packages, I can see that the WKND packages have been recently installed.
Next, I want to import the project into my IDE. In this video and throughout the rest of the tutorial we’ll be using the visual studio code IDE. There are also videos available for setting this up, using eclipse and IntelliJ.
I’ll go ahead and open up Visual Studio code and navigate to the folder location of the AEM guides WKND project.
In the explorer side rail, I can see the project’s structure.
Several extensions make AEM development much easier. One of them is the Java extension pack. As we’ll see in a moment, Java is the language used for server side back-end logic with sling models.
You may also want to install the Maven for Java extension to make it easier to use the IDE for building with Maven.
Other commonly used extensions are VScode, AEM sync and AEM sync. These extensions allow you to individually sync files from the project with a running instance of AEM without having to perform a full Maven build. This is much faster for quickly testing small changes and is quite handy when working with HDL files which AM’s server side templating language. Both are popular extensions. In this video I’ll be using VScode AEM sync but I encourage you to try both of them. Lastly, I also have an extension for language support for HTL, which provides some auto complete and syntax highlighting for HTL files.
Visual studio codes marketplace has many more extensions that I’d encourage you to explore. -

Open a new command-line terminal and perform the following actions.

  1. In an empty directory, clone the aem-guides-wknd repository:

    code language-shell
    $ git clone --branch tutorial/component-basics-start --single-branch
    note note
    Optionally, you can continue using the project generated in the previous chapter, Project Setup.
  2. Navigate into the aem-guides-wknd folder.

    code language-shell
    $ cd aem-guides-wknd
  3. Build and deploy the project to a local instance of AEM with the following command:

    code language-shell
    $ mvn clean install -PautoInstallSinglePackage
    note note
    If using AEM 6.5 or 6.4, append the classic profile to any Maven commands.
    code language-shell
    $ mvn clean install -PautoInstallSinglePackage -Pclassic
  4. Import the project into your preferred IDE by following the instructions to set up a local development environment.

Component Authoring component-authoring

Components can be thought of as small modular building blocks of a web page. In order to reuse components, the components must be configurable. This is accomplished via the author dialog. Next let’s author a simple component and inspect how values from the dialog are persisted in AEM.

All right, so now we’re going to create a new page. So, from the AEM sites console, I’m just going to navigate under weekend US. And then under the homepage, EN, we’ll create a new page and we’re just going to choose the content page template. And for the title, I’ll just call this component basics.
Then we can go ahead and create our page and open it up.
Now all AEM pages that are enabled for authoring. We’ll have this drop zone otherwise known as layout container. And this is the area of the page that a content author can add new components. So, open up the side panel and we’ll browse to the available components.
And I’m just going to select the hello world component. The hello world component is included by the archetype really just for illustration purposes in order to gain an understanding of how components work. So, you can see that it’s already outputting some information as soon as we add it to the page. And this is a result of some backend code that we’ll look at later in the tutorial. Now that I’ve added the component, we can configure it by selecting the component and clicking the wrench icon. In order to make components reusable, almost all of them have configurations. In this case, it’s a simple text field. So, I’ll enter some information in the text field and then save the changes by clicking done. Notice that the component now renders a simple message. This example shows how a component can take user entered information and render it as part of the page.
Let’s add a second hello world component.
And I’ll configure this one, except this time let’s enter a different message to be displayed. So, we’ll call it hello world two. And then we can see the result. So that’s an example of how dialogues facilitate authoring and how you can reuse the same component on the same page but with different outputs. Now let’s take a look at the JCR or the Java content repository and see how the text properties of these components are persisted.
Now, an easy way to do this is to switch into developer mode. And developer mode is a tool intended to be used really just on a local environment. And it provides a lot of useful information in diagnostics of all of the components that currently make up the page. So, you’ll notice that in this side panel, there’s a lot more components listed here than there are in the content tree.
And if we select the hello world component it will automatically expand the tree and show us that instance of the component.
And so, you can see we’ve got a responsive grid and then we’ve got our hello world component. Both of them listed here.
And if we select this button to view details it’ll actually show us the content path to this exact component.
And if we click that, that’s going to open up CRXDE Lite which is a view into the backend JCL repository of AEM.
And notice that we can see both the hello world components And it’s under our component basics page.
In the text, you can see that text property that we listed here, but that’s really the only data that’s stored. And if we click our second hello world component, you can see the second text property.
So where does the rest of the rendering information come from? Well, it actually gets derived from this line string resource type which points to weekend components, content, hello world. And so that is the actual rendering script that’s used to render the component on the page.
So, let’s take a look at where that rendering script lives. Now, since this is code it’s actually going to live under the apps folder. So, in CRXDE Lite, if you expand the apps folder under weekend components, content, and hello world you can actually see this is the hello world code component and you can see the hello world.HTML file in there. And so, this is the actual rendering script written in HTML. And this is responsible for outputting all of the content that you see when you add that component to the page. And that’s what we’re going to look at next. -

Below are the high-level steps performed in the above video.

  1. Create a page named Component Basics beneath WKND Site > US > en.
  2. Add the Hello World Component to the newly created page.
  3. Open the dialog for the component and enter some text. Save the changes to see the message displayed on the page.
  4. Switch in to developer mode and view the Content Path in CRXDE-Lite and inspect the properties of the component instance.
  5. Use CRXDE-Lite to view the cq:dialog and helloworld.html script from /apps/wknd/components/content/helloworld.

HTL (HTML Template Language) and Dialogs htl-dialogs

HTML Template Language or HTL is a light-weight, server-side templating language used by AEM components to render content.

Dialogs define the configurations available that can be made for a component.

Next let’s update the HelloWorld HTL script to display an extra greeting before the text message.

HTML template language or HTL, is a lightweight server-side templating language used by AEM components to render content.
Next, we will update the HelloWorld HTL script in order to understand how this can work.
I’ll return to my IDE and I’ll navigate to the UI apps folder. This is a Maven module that maps to the apps tree that we saw in CRXDE lite. Expanding the folder structure, I can find the HelloWorld folder. Again, you can see that this structure maps to the structure we saw in CRXDE lite.
Next, I’ll open the HelloWorld.HTML file. This is our HTL script that will render the content. So, let’s make a small change to the script. I’ll modify the text of this static title, and I’ll change the H2 tag to be H1.
Next, I’ll use the developer tools to synchronize the change with my running instance of AEM.
I’ll return to the browser, refresh the page, and view the change.
Let’s return to the IDE.
You’ll notice several XML files. Now the JCR is made up of a series of nodes and properties which we saw in CRXDE lite. When these nodes and properties are represented on the file system, they take the form of XML. This first XML file is the components definition. The JCR title defines the components title that will be seen in the author UI. And the component group defines the grouping of components.
Next, let’s inspect the dialogue. The dialogue is also a series of nodes and properties and thus is represented as XML in our IDE.
Recall that the HelloWorld components dialogue had a single text field for a text. And this is represented here. The label for the field is set here and this property, sling resource super type, defines that this will be rendered as a text field. The name defines what this property will be saved as. Let’s create a new text field called Title. I’ll copy this text block and rename it to be Title. The field label will also be set to title. And finally, the name will be set to ./title.
I’ll save the changes and re-sync the file with AEM. I’ll return to the browser. And, after I open up the dialogue of one of our components.
Notice that we now have an additional text field for Title. I can go ahead and enter a new Title, and save the changes. However, after saving, you can see that nothing is rendered in the output. This is because, we have not yet referenced this Title text field from the HTL script.
Let’s return to the IDE. And again, I’ll reopen the HelloWorld HTL script. Instead of this static text here, I’ll replace it with dollar sign, and the curly braces, and then properties.title. The dollar sign and the curly braces indicate that this is dynamic code, that should be interpreted by the HTL compiler. Properties is a shorthand object that references any of the properties saved by the component. And finally, Title is the name we gave our text field in the dialogue. Once again, I’ll save the changes, and again export the changes to the AEM instance. It is possible to set the IDE to automatically sync any file changes on save. But for this video, I’ve set it to be a manual export each time. Now, when we return to the browser, and refresh a page, we can see the updated change. Instead of static texts, we can now use the dialogue to dynamically update the component.
This is a simple example but hopefully, you can start to see how easy it is to update these dialogues, and map the properties to values in the HTL script. The core components are all open-sourced, and are a great reference to see all of the different types of fields that can be used to generate a dialog. Using the properties object, is great for displaying simple text fields. But what if we want to do something more complex, like apply some business logic before rendering the output. This is where sling models come into play. And that’s what we’re going to look at next. -

Below are the high-level steps performed in the above video.

  1. Switch to the IDE and open the project to the ui.apps module.

  2. Open the helloworld.html file and update the HTML Markup.

  3. Use the IDE tools like VSCode AEM Sync to synchronize the file change with the local AEM instance.

  4. Return to the browser and observe the component render has changed.

  5. Open the .content.xml file that defines the dialog for the HelloWorld component at:

    code language-plain
  6. Update the dialog to add an extra textfield named Title with a name of ./title:

    code language-xml
    <?xml version="1.0" encoding="UTF-8"?>
    <jcr:root xmlns:sling="" xmlns:cq="" xmlns:jcr="" xmlns:nt=""
            <items jcr:primaryType="nt:unstructured">
                    <items jcr:primaryType="nt:unstructured">
  7. Reopen the file helloworld.html, which represents the main HTL script responsible for rendering the HelloWorld component from below path:

    code language-plain
  8. Update helloworld.html to render the value of the Greeting textfield as part of an H1 tag:

    code language-html
    <div class="cmp-helloworld" data-cmp-is="helloworld">
        <h1 class="cmp-helloworld__title">${properties.title}</h1>
  9. Deploy the changes to a local instance of AEM using the developer plugin or using your Maven skills.

Sling Models sling-models

Sling Models are annotation driven Java™ “POJOs” (Plain Old Java™ Objects) that facilitate the mapping of data from the JCR to Java™ variables. They also provide several other niceties when developing in the context of AEM.

Next, let’s make some updates to the HelloWorldModel Sling Model in order to apply some business logic to the values stored in the JCR before outputting them to the page.

Sling Models are annotation driven Java POJOs or Plain Old Java Objects. Sling Models make it easy to map properties from the JCR to Java variables. Any complex business logic that a component might require should be implemented in a Sling Model. This ensures that the component’s HTL script remains simple and promotes a clean separation of concerns. In my IDE I have opened the HelloWorld.HTML file. This line data-sly-use sets a variable model. And the model variable is assigned through the Java class HelloWorld model, and this is the Sling Model for the component. Typically, there’s a single Sling Model per AEM component. And all the Java code is maintained in the Core Module, so let’s navigate there. Expanding the Core Module, you can see the package location of the models, and this is generated by the AEM project archetype. Beneath models, you can see the Helloworld file. Now I’ll go ahead and open that up. And sling models are annotation driven. The at model annotation defines the class as a sling model. Next, we have several annotations to reference various properties in services in AEM. The ad post construct init method is the first method called when the Sling Model is initialized and this code writes out a few lines detailing the components resource type, the current page and the current environment of AEM. Next, let’s add some business logic to the title property we added to the dialogue and HTL earlier. First, we want to read the value of title entered via the dialogue and stored in the JCR. We’ll use the add value map annotation to quickly map the value of title to a Java variable named title. The convention of using title as the Java variable is important as it needs to match the property name stored in the JCR exactly.
Here, the injection strategy is set to optional since when the component is first added to the page, the title property will not be set. It’s also possible to set a default injection strategy to cover all properties in the model. Let’s go ahead and do that now, so we no longer need to add additional annotations.
Next, I’ll add another add value map annotation to get the value of the property text.
Again, we’re following the convention where the Java variable text matches the JCR property text.
Let’s go ahead and add some business logic to the components title. I’ll create a new getter method called “get title” that will return a string. This method takes no parameters, and if you’re planning on calling a method from the HDL script, it’s important to note that it cannot accept any parameters. Using the string utils class, I’ll check to see if the value of the title property is not blank.
And if it’s not blank, I’ll simply return that value. And this will be the value that the user has entered. However, if the text property is blank or set to null, I’ll return a static string called default value here.
Let’s add a simple transformation to the text property. I’ll create another getter method called getText, that will return a string. I’ll use the string utils class once again to check if the property is set and this time, if it is set, I’m going to transform the text to uppercase.
Otherwise, if the property is not set I’ll simply return null. To deploy these changes to AEM, I need to build and compile the Java bundle. I’ll open up a terminal window and navigate into the core directory. Here I’ll use the Maven profile auto installed bundle so that I can just compile and deploy the Java bundle.
Next, we need to update the HDL script to reference these two new methods from our Sling Model. I’ll go ahead and reopen the HelloWorld dot HTML file.
First, I’ll move the reference to the HelloWorld model to the outer most div in the script. This allows us to use the HelloWorld model anywhere within the script.
Next, I’ll replace the properties dot title with model dot title. Here, model is the reference to the Sling Model, and title is a reference to the get title method implemented earlier. When referencing getter methods and HDL, you drop the get portion of the method and lowercase the first letter.
Let’s also add a reference to the getText method from the model. I’ll start by copying and pasting this block here, so that we can compare the raw text property with the output from the sling model. I’ll update the properties to be model, here and here, and then I’ll update the label so that we know it came from the model.
Data slide test is a shorthand If statement by HTL, if the value is null or blank, the HTML block will not be rendered at all.
All right, I’ll go ahead and sync the changes to the HTL file with AEM and returning to the browser and refreshing the page, we can see the updated text method. Here the text value is displayed as the user entered, in here the Sling Model is called, and so the value is transformed to uppercase.
Let’s check the logic of the get title method. I’ll edit the component and remove the value in the title field.
After saving the changes, we can now see that the component has fallen back to use the default value that we provided. -
  1. Open the file, which is the Sling Model used with the HelloWorld component.

    code language-plain
  2. Add the following import statements:

    code language-java
    import org.apache.commons.lang3.StringUtils;
  3. Update the @Model annotation to use a DefaultInjectionStrategy:

    code language-java
    @Model(adaptables = Resource.class,
       defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
       public class HelloWorldModel {
  4. Add the following lines to the HelloWorldModel class to map the values of the component’s JCR properties title and text to Java™ variables:

    code language-java
    @Model(adaptables = Resource.class,
    defaultInjectionStrategy = DefaultInjectionStrategy.OPTIONAL)
    public class HelloWorldModel {
        private String title;
        private String text;
        protected void init() {
  5. Add the following method getTitle() to the HelloWorldModel class, that returns the value of the property named title. This method adds the additional logic to return a String value of “Default Value here!” if the property title is null or blank:

    code language-java
    * @return the value of title, if null or blank returns "Default Value here!"
    public String getTitle() {
        return StringUtils.isNotBlank(title) ? title : "Default Value here!";
  6. Add the following method getText() to the HelloWorldModel class, that returns the value of the property named text. This method transforms the String to all uppercase characters.

    code language-java
        * @return All caps variation of the text value
    public String getText() {
        return StringUtils.isNotBlank(this.text) ? this.text.toUpperCase() : null;
  7. Build and deploy the bundle from the core module:

    code language-shell
    $ cd core
    $ mvn clean install -PautoInstallBundle
    note note
    For AEM 6.4/6.5 use mvn clean install -PautoInstallBundle -Pclassic
  8. Update the file helloworld.html at aem-guides-wknd.ui.apps/src/main/content/jcr_root/apps/wknd/components/content/helloworld/helloworld.html to use the newly created methods of the HelloWorld model.

    The HelloWorld model is instantiated for this component instance via the HTL directive: data-sly-use.model="com.adobe.aem.guides.wknd.core.models.HelloWorldModel", saving the instance to the variable model.

    The HelloWorld model instance is now available in the HTL via the model variable using the HelloWord. These methods invocations can use shortened method syntax for example: ${model.getTitle()} can be shorted to ${model.title}.

    Similarly, all HTL scripts are injected with global objects that can be accessed using the same syntax as the Sling Model objects.

    code language-html
    <div class="cmp-helloworld" data-cmp-is="helloworld"
        <h1 class="cmp-helloworld__title">${model.title}</h1>
        <div class="cmp-helloworld__item" data-sly-test="${properties.text}">
            <p class="cmp-helloworld__item-label">Text property:</p>
            <pre class="cmp-helloworld__item-output" data-cmp-hook-helloworld="property">${properties.text}</pre>
        <div class="cmp-helloworld__item" data-sly-test="${model.text}">
            <p class="cmp-helloworld__item-label">Sling Model getText() property:</p>
            <pre class="cmp-helloworld__item-output" data-cmp-hook-helloworld="property">${model.text}</pre>
  9. Deploy the changes to a local instance of AEM using the Eclipse Developer plugin or using your Maven skills.

Client-Side Libraries client-side-libraries

Client-Side Libraries, clientlibs for short, provides a mechanism to organize and manage CSS and JavaScript files necessary for an AEM Sites implementation. Client-side libraries are the standard way to include CSS and JavaScript on a page in AEM.

The ui.frontend module is a de-coupled webpack project that is integrated into the build process. This enables the use of popular front-end libraries like Sass, LESS, and TypeScript. The ui.frontend module is explored in more depth in the Client-Side Libraries chapter.

Next, update the CSS styles for the HelloWorld component.

Client-Side Libraries provide a mechanism for including CSS and JavaScript on a page in AEM. Modern day web implementations use a variety of frontend frameworks and libraries, ranging from CSS pre-processors like SASS and LESS, as well as JavaScript libraries from NPM modules. The AEM project archetype comes with a UI.Frontend module, that is a decoupled webpack project, integrated into the overall build.
The way it works is that the webpack project is built like normal, and then the compiled CSS and JavaScript artifacts are turned into AEM Client Libraries, which are then synced into AEM. The integration is set up to use webpack, but really any bundler could be used and integrated in the same way. So you could use something like Gulp or Parcel to manage the frontend source files, and then integrate them with the overall AEM project using the same pattern that we have in the UI.Frontend module.
The end to end process is explored in more detail in the chapter on Client-Side Libraries. For now, let’s make a small change, just so that we can see it in action.
First, I’ll open a new terminal window and navigate into the UI.Frontend directory. I’ll run the command npm run watch, and this command will watch the source files and the webpack project for any changes. And it will then compile those changes into an AEM Client Library and push them into AEM.
Next, I’ll expand the source folder. And under components, I’ll open the file _helloworld.scss. This is a partial SASS file, and it’s designed just to style the helloworld component. You can see the stubbed out rules for each of the classes used in the component.
A best practice is to target class names instead of DOM elements. All right, so let’s add a new rule to color the title red.
So I’ll update this cmp-helloworld_title, and I’ll put in a rule color, ;red, and then I’ll save the changes. And you can see in the terminal that it’s being synced to my AEM environment. And if we switch to the browser, there we can see our updated colors. And so using this process, I can new to iterate against my style changes, making changes, and seeing those reflected in the AEM environment. And I can continue this process until I’m happy with the style components.
So let’s inspect the integration a little further. The webpack build is going to compile our source folders and compile them into a single CSS file in the dist folder. And here you can see all of the different styles. And here is our cmp-helloworld color. And then this file is transformed into a Client Library, which is pushed into the UI apps module. And so you can see here, we can see the exact same CSS file. And then from here, the UI apps module is pushed into AEM.
All right, so now let’s inspect the Client Library inclusion on the AEM page. And the first thing I’ll do is view the page as published. And this is a good practice if you’re doing frontend development because it will remove the AEM editor, JavaScript, and CSS, and just make it easier to debug and kind of inspect the page. So if we view the page source, here you can see we got our client lib site. And this is our CSS file here. And if we click into it, then we can go ahead and see the compiled CSS being referenced.
So since we’re using the webpack-dev-server, the CSS has not been minimized, but when you run the project using a full Maven build, then the CSS and JavaScript would be minified, and optimized, and be much smaller. All right, so that’s it for this short video on introduction to AEM Client Libraries as part of the component basics tutorial. . -

Below are the high-level steps performed in the above video.

  1. Open a terminal window and navigate into the ui.frontend directory

  2. Being in ui.frontend directory run the npm install npm-run-all --save-dev command to install the npm-run-all node module. This step is required on Archetype 39 generated AEM project, in upcoming Archetype version this is not required.

  3. Next, run the npm run watch command:

    code language-shell
    $ npm run watch
  4. Switch to the IDE and open the project to the ui.frontend module.

  5. Open the file ui.frontend/src/main/webpack/components/_helloworld.scss.

  6. Update the file to display a red title:

    code language-scss
    .cmp-helloworld {}
    .cmp-helloworld__title {
        color: red;
  7. In the terminal, you should see activity indicating that the ui.frontend module is compiling and syncing the changes with the local instance of AEM.

    code language-shell
    Entrypoint site 214 KiB = clientlib-site/site.css 8.45 KiB clientlib-site/site.js 206 KiB
    2022-02-22 17:28:51: webpack 5.69.1 compiled successfully in 119 ms
    + jcr_root/apps/wknd/clientlibs/clientlib-site/css/site.css
    + jcr_root/apps/wknd/clientlibs/clientlib-site/css
    + jcr_root/apps/wknd/clientlibs/clientlib-site/js/site.js
    + jcr_root/apps/wknd/clientlibs/clientlib-site/js
    + jcr_root/apps/wknd/clientlibs/clientlib-site
    + jcr_root/apps/wknd/clientlibs/clientlib-dependencies/css.txt
    + jcr_root/apps/wknd/clientlibs/clientlib-dependencies/js.txt
    + jcr_root/apps/wknd/clientlibs/clientlib-dependencies
  8. Return to the browser and observe that the title color has changed.

    Component Basics update

Congratulations! congratulations

Congratulations, you have learned the basics of component development in Adobe Experience Manager!

Next Steps next-steps

Get familiar with Adobe Experience Manager pages and templates in the next chapter Pages and Templates. Understand how Core Components are proxied into the project and learn advanced policy configurations of editable templates to build out a well-structured Article Page template.

View the finished code on GitHub or review and deploy the code locally at on the Git branch tutorial/component-basics-solution.