For AI agents: the complete documentation index is available at https://nhanbe.id.vn/llms.txt, the full documentation bundle is available at https://nhanbe.id.vn/llms-full.txt, and this page is available as Markdown at https://nhanbe.id.vn/aem/foundation/client-libraries.md.

Client libraries AEM basic

Client Libraries

Structure

/apps/<site>/clientlibs/
└── clientlib-base/
    ├── .content.xml          # cq:ClientLibraryFolder
    ├── css.txt               # CSS inclusion order
    ├── js.txt                # JS inclusion order
    ├── css/
    │   ├── reset.css
    │   ├── variables.css
    │   └── base.css
    └── js/
        ├── utils.js
        └── main.js

.content.xml:

<?xml version="1.0" encoding="UTF-8"?>
<jcr:root xmlns:cq="http://www.day.com/jcr/cq/1.0"
          xmlns:jcr="http://www.jcp.org/jcr/1.0"
          jcr:primaryType="cq:ClientLibraryFolder"
          categories="[<site>.base]"
          allowProxy="{Boolean}true"/>
PropertyPurpose
categoriesnames the category, included in HTL using this string
allowProxyserves via /etc.clientlibs/ — always enable, even on-prem
dependenciesloads other categories first (separate requests)
embedmerges other categories into this bundle (fewer requests)

css.txt / js.txt

css.txt:

#base=css
reset.css
variables.css
base.css

js.txt:

#base=js
utils.js
main.js

File order = concatenation order. #base= sets the target folder prefix. Unlisted files are omitted from the bundle.

Including in HTL

<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html">
  <!-- CSS in <head> -->
  <sly data-sly-call="${clientlib.css @ categories='<site>.base'}"/>

  <!-- JS before </body> -->
  <sly data-sly-call="${clientlib.js @ categories='<site>.base'}"/>

  <!-- Both -->
  <sly data-sly-call="${clientlib.all @ categories='<site>.base'}"/>
</sly>

Multiple categories use an array: categories=['<site>.base', '<site>.components'].

Dependencies vs Embed

dependencies = keeps separate requests to ensure load order. embed = combines into 1 request. Use embed for the site's final bundle, dependencies for shared libs requiring independent caching.

dependencies:

<jcr:root
  jcr:primaryType="cq:ClientLibraryFolder"
  categories="[<site>.components]"
  dependencies="[<site>.base]"
  allowProxy="{Boolean}true"/>

→ 2 separate requests: &lt;site&gt;.base → &lt;site&gt;.components

embed:

<jcr:root
  jcr:primaryType="cq:ClientLibraryFolder"
  categories="[<site>.site]"
  embed="[<site>.base,<site>.components]"
  allowProxy="{Boolean}true"/>

→ 1 request: &lt;site&gt;.site (contains both base + components)

Component clientlib

/apps/<site>/components/hero/
├── .content.xml
├── hero.html
├── _cq_dialog/.content.xml
└── clientlibs/
    ├── .content.xml
    ├── css.txt
    ├── js.txt
    ├── css/hero.css
    └── js/hero.js

The .content.xml of a component clientlib is similar, differing only in categories. Multiple components can use the same category (&lt;site&gt;.components) to bundle together.

ui.frontend Module

SCSS/TS builds outside AEM, output copies to ui.apps/.../clientlibs/. Standard flow:

  1. npm run build inside ui.frontend/
  2. Output copied to ui.apps/src/main/content/jcr_root/apps/&lt;site&gt;/clientlibs/
  3. Deploy Maven package into AEM

Dev loop: aem-clientlib-generator (standard archetype) or aemsync.

Naming Conventions

CategoryContentLoad
&lt;site&gt;.basereset/fonts/variables/globalevery page
&lt;site&gt;.componentsper-component CSS/JSevery page
&lt;site&gt;.siteembeds base + componentsevery page (single include)
&lt;site&gt;.dependenciesthird-party libssite dependency

Debugging

  • ?debugClientLibs=true — view unminified libs loading on page
  • /libs/granite/ui/content/dumplibs.rebuild.html — invalidate cache + rebuild (use when changes do not reflect)
  • Check direct proxy file: http://localhost:4502/etc.clientlibs/<site>/clientlibs/clientlib-base.css
SymptomCheckFix
CSS/JS not loadingcategory string in HTLmatch categories in .content.xml
404 on /etc.clientlibs/...allowProxyset {Boolean}true + deploy
Deployed but changes missingbrowser/dispatcher cache + rebuildinvalidate + rebuild libs
Incorrect load orderdependencies/embedmove shared libs to dependencies; bundle app libs with embed
JS file does not executejs.txt + path + #baseadd file to js.txt, verify location