AEM Clientlibs Complete Notes
Before we jump into the AEM Clientlibs, let’s first look at how JavaScript and CSS files are managed in a typical web development project.
Let’s take the example of a simple HTML-based project. You might be tempted to include multiple CSS and JavaScript files separately in the <head> tag or right before ‘</body>’ as follows:
<head>
<link rel="stylesheet" href="styles/reset.css">
<link rel="stylesheet" href="styles/layout.css">
<link rel="stylesheet" href="styles/theme.css">
<script src="scripts/jquery.js"></script>
<script src="scripts/main.js"></script>
<script src="scripts/slider.js"></script>
</head>
1. This method of including styles has its disadvantages
- There is a default restriction that every CSS and JS file represents a new request, which can impact the loading speed of the intended page.
- If main.js is waiting on jquery.js, and the loading order is reversed, it could lead to a site failure.
- A developer, irrespective of skill level, has to manually combine and minify huge amounts of files to make the application perform better.
- If changes are made in a file, there’s always a chance that the user can access the previous cached file unless the names of the files are altered.
2. How AEM Solves This with Clientlibs
AEM makes it possible to create and edit multiple styles and scripts with ease using the AEM Client Libraries (Clientlibs). Instead of manually adding multiple scripts and styles to be fetched and added, AEM merges them and applies minification, dependency handling, and improved caching.
3. What are Clientlibs?
- In Adobe Experience Manager (AEM), Client Libraries (Clientlibs) manage front-end files like JavaScript, CSS, images, and fonts. Instead of manually adding multiple files in different places, Clientlibs helps bundle and organize them efficiently.
- Clientlibs improve page performance by reducing the number of HTTP requests and ensuring that scripts and styles load in the right order.
4. Key Features of clientlibs
- Clientlibs combine multiple JavaScript and CSS files into a single file to reduce HTTP requests.
- Minification removes unnecessary characters (like spaces and comments) from the code to reduce file size.
- Clientlibs can declare dependencies on other clientlibs using the dependencies property. This ensures that files are loaded in the correct order.
- AEM automatically handles caching for client libraries, assigning a unique fingerprint (hash) to the file name based on its content. This ensures browsers only fetch updated versions when changes occur.
- Different clientlibs can be created for different themes or device types (e.g., mobile vs desktop).
5. Properties of clientlibs
1. categories
(Required)
- This is used to identify a Clientlib name uniquely.
- Clientlibs can have multiple categories, allowing them to be included in different places.
Name: categories
Type: String[]
Value: [your-project.category]
2. allowProxy
(Boolean)
- The allowProxy property determines whether a Client Library can be accessed via AEM’s Client Library Proxy Servlet.
- allowProxy = true: The Client Library can be accessed via /etc.clientlibs, which hides its actual location and improves security.
- allowProxy = false: The Client Library must be accessed directly from its path (e.g., /apps/my-project/clientlibs).
When to Use allowProxy = false?
- These are not meant for public access but are used internally by components or templates.
- Setting allowProxy=false restricts access, ensuring the library is only available within the application scope.
- This is useful for sensitive resources or development-specific libraries.
Name: allowProxy
Type: Boolean
Value: true
3. dependencies
(Optional)
- Dependencies are used to load other clientlibs before our clientlibs.
EXAMPLE:
- custom-clientlibs and desktop clientlibs, so I want to load the desktop clientlibs before the custom-clientlibs to activate, we use dependencies.
- Enables shared resources across libraries.
Name: dependencies
Type: String[]
Value: [desktop]
4. embed
(Optional)
- Combines multiple files into one to reduce requests.
- embed helps us combine all client libraries and load them as a single CSS and JS file. If A category embeds two different categories B and C. The resulting CSS and JS will have one single JS and CSS file.
Name: embed
Type: String[]
Value: [your-project.base, your-project.components]
5. jsProcessor
(Optional)
min:gcc
– Google Closure Compiler minificationmin:yui
– YUI Compressor minificationmin:none
– No modification
Name: jsProcessor
Type: String[]
Value: [min:yui
6. cssProcessor
(Optional)
min:none
– No minificationmin:yui
– YUI-based minificationmin:cssnano
– Uses CSSNano
Name: cssProcessor
Type: String[]
Value: [min:cssnano]
7. replaces
(Optional)
- This will replace another ClientLib completely, useful for overriding default AEM libraries.
Name: replaces
Type: String[]
Value: [cq.desktop]
8. serializationType
(Optional)
- Controls how ClientLib files are merged and delivered.
flat
– Delivers files as individual requests.deep
– Merges all files into a single response.
Name: serializationType
Type: String
Value: flat
6. How to Create Clientlibs using crx/de
- Go to CRX/de → http://localhost:4502/crx/de/index.jsp
- Go inside the apps folder /apps/project-name -> Go to clientlibs
- Right-click on the clientlibs folder and click on create → node
- Select node type has cq: ClientLibraryFolder and Name can be anything.
- Create CSS and JS folders to place respective CSS and JS files.
- Create sample.js and sample.css files.
- Create the below js.txt file inside the JS folder to declare the file names that need to be loaded as part of the custom-clientlibs category.
- base=js represents the .js files’ root folder name.
#base=js
sample.js
- Create the css.txt file inside the CSS folder to declare the file names that need to be loaded as part of practice. custom-clientlibs category.
- base=css represents the CSS file’s root folder name.
#base=csssite.css
- Once you are done with creating the folder structure,
- you can test whether clientlibs are working or not, hit the below URL to test.
- Example → http://localhost:4502/apps/AEM-DeveloperResource/clientlibs/custom_clientlibs.js
- Create one more client library folder, by following the above steps.
- Create the below properties under custom-clientlibs.
- Once you added all the properties you need, load your clientlibs.
- Go to page http://localhost:4502/editor.html/content/AEM-DeveloperResource/us/en/search.html
The Above Stepup ensures:
- desktop loads before the site ClientLibs.
- The ClientLib is publicly accessible via
/etc.clientlibs
. - Minified CSS & JS for better performance.
7. What is /etc.clientlibs in AEM?
When you work in AEM, you might find /etc. clientlibs on your path. This is a proxy path where AEM serves client-side resources like JavaScript, CSS files, fonts, and images without revealing their exact locations in /apps or /libs.
8. Why Do We Need /etc.clientlibs?
For security purposes, AEM does not allow direct access to resources in /apps and /libs folders. So, if you try to access a CSS file or a JS file within /apps, it will return an error. This is where /etc. clientlibs comes into play. Its purpose is to act as a middleman that unveils those resources while still hiding the actual structure.
9. Key Benefits of Using /etc.clientlibs:
Security: Mask the actual paths and prevent access to unauthorized users.
Performance: Speeds up the process of minification, compression, and caching.
Proper Script Loading: Ensure scripts are loaded in the right order by handling dependencies.
Lazy Loading Support: Clientlibs can be loaded when necessary.
10. How Does /etc.clientlibs Work?
Let’s imagine you have a Client Library (clientlibs-site) that is in /apps and is located like this:
/apps/your-project/clientlibs-site
- jcr:primaryType = "cq:ClientLibraryFolder"
- categories = ["your-project.site"]- allowProxy = true
So, since allowProxy = true, AEM exposes this Client Library by default in
/etc.clientlibs/your-project/clientlibs-site.min.css
/etc.clientlibs/your-project/clientlibs-site.min.js
11. How Many Ways can we include ClientLibs on the page
- Add clientlibs to page policies
AEM allows you to include Client Libraries (ClientLibs) at the template level using Page Policies. This ensures that specific styles and scripts are applied consistently across pages using the same template.
- Go to AEM > Tools > General > Templates.
- Open the template you want to modify.
- Click on Structure Mode (to edit the template structure).
- Select the Page Root (Layout Container or Responsive Grid).
- In the right panel, click on the Policy
- If a policy is not assigned, create a new one by clicking on “Add Policy.”
- Give the policy a meaningful name.
- add your clientlibs → category name and click on done.
- Once added, your category name, refresh the page and go to view page source
- You will find desktop is loaded first, and custom-clientlibs is loaded next, this worked by adding dependencies.
- clientlibs is a multiple value property which allows us to load multiple client categories at the same time.
- clientlibsJsHead allows us to load only JS for the mentioned client category.
- clientlibsCssHead allows us to load only CSS for the mentioned client category.
<page jcr:primaryType="nt:unstructured">
<policy
jcr:description="Include Client libraries."
jcr:primaryType="nt:unstructured"
jcr:title="Page Component"
sling:resourceType="wcm/core/components/policy/policy"
clientlibs="[desktop,custom-clientlibs]"
clientlibsJsHead="desktop">
<jcr:content jcr:primaryType="nt:unstructured"/>
</policy>
</page>
2. Adding ClientLibs to a Page Using JavaScript Use-API
- AEM’s JavaScript Use-API allows developers to dynamically add Client Libraries (ClientLibs) to a page within a component.
- Below is the code snippet to load both JS and CSS for the mentioned clientlib category as shown below:
<sly data-sly-use.clientlib="core/wcm/components/commons/v1/templates/clientlib.html">
<sly data-sly-call="${clientlib.all @ categories='AEM-DeveloperResource.base'}"/>
</sly>
- clientlib.all to load both CSS and js files
- clientlib.css to load CSS files. The below statement helps us to load CSS as part of the header.
<sly data-sly-call=”${clientlib.css @ categories=’practice.base’}”/>
- clientlib.js to load JS files. The below statement helps us to load JS as part of the footer.
<sly data-sly-call=”${clientlib.js@ categories=’practice.base’}”/>
3. Loading Clientlibs Dynamically for Specific Components
- You might want to load specific client libraries only when a particular component is rendered on a page. This approach optimizes performance by reducing unnecessary resource loading.
- Using Sling Models and data-sly-test
- You can dynamically include clientlibs in your HTL templates based on conditions
<sly data-sly-use.clientlib="core/wcm/components/commons/v1/templates/clientlib.html">
<!-- Load clientlibs only if the condition is true -->
<sly data-sly-test="${component.enabled}">
<sly data-sly-call="${clientlib.all @ categories='your-component-clientlib'}" />
</sly>
</sly>
data-sly-use.clientlib:
- This imports the clientlib helper script for dynamically including client libraries
data-sly-test:
- This checks the condition (e.g., ${component.enabled}) to determine if the clientlib should be included.
- Replace the component enabled with the actual logic (e.g., checking a component property or context variable).
data-sly-call:
- Loads the required client libraries using their category name (your-component-clientlib).
- Use clientlib. all to include both CSS and JS.
- Alternatively, use clientlib.css or clientlib.js to load only CSS or JS, respectively
<sly data-sly-call="${clientlib.css @ categories='your-component-clientlib'}" />
<sly data-sly-call="${clientlib.js @ categories='your-component-clientlib'}" />
4. Loading Client Library When a Dialog Opens
When working with AEM components, sometimes we need to load additional JavaScript and CSS specifically when a component’s dialog opens in the Touch UI. This is useful for initializing custom scripts, adding event listeners, or applying styles that are relevant only within the dialog.
12. Steps to Load Client Library When a Dialog Opens
- Create the Client Library Folder inside your component
/apps/custom/division/components/button/clientlibs

Inside that, create the following files:
- Create Js folder
js.txt
→ To include JavaScript files.- Add custom JavaScript in a file like
dialog.js
inside thejs
folder. - Add Categories name under folder hierarchy
- add this property
allowProxy = true
Example code: when you the component dialog you should console error.
$(document).on("dialog-ready", function() {
console.log("Dialog has opened!");
});

4. Open the component’s dialog in AEM, and the client library should now load when the dialog opens.
Use Cases of Loading Client Library in Dialog
- Initializing custom scripts when a dialog opens.
- Enhancing authoring experience with additional UI elements.
- Adding validation or event listeners dynamically.
5. Environment-Specific Loading
You can conditionally load clientlibs in your HTL scripts based on
- Editor mode
- preview mode
<sly data-sly-use.clientlib="core/wcm/components/commons/v1/templates/clientlib.html">
<sly data-sly-test="${wcmmode.edit}">
<!-- Load editor-specific clientlibs -->
<sly data-sly-call="${clientlib.all @ categories='editor-clientlibs'}" />
</sly>
<sly data-sly-test="${wcmmode.disabled}">
<!-- Load user-specific clientlibs -->
<sly data-sly-call="${clientlib.all @ categories='user-clientlibs'}" />
</sly>
</sly>
Every great discussion starts with a simple thought! If you enjoyed this article, found it useful, or have any questions, let’s talk! I’d love to hear from you.
For more updates, tips, and engaging conversations, connect with me on Medium and LinkedIn. Let’s keep learning together! 🚀✨