AEM Client Libraries Bundle JavaScript and CSS per page type, load late and cache for a long time
Client libraries are the mechanism Adobe Experience Manager uses to bundle and deliver JavaScript and CSS. They determine how much code a browser has to load and execute before a page responds. On mature platforms, every page often loads the same large bundle. This burdens the main thread and thus Interaction to Next Paint, the time between an input and the visible response. This page shows how to split client libraries per page type, load them late and cache them for a long time.
In the AEM Core Web Vitals Benchmark 2026, 74.4 percent of AEM websites in the DACH region reach good input responsiveness on mobile. Among the websites that fail the Core Web Vitals in our own data collection, 47.2 percent miss the threshold for responsiveness.
Client libraries in AEM: categories, embed and dependencies
A client library is a folder of type cq:ClientLibraryFolder with JavaScript and CSS files. Three properties control what finally reaches the browser:
categoriesgives the library a name that pages and components use to include it.embedmerges the code of other categories into the library's own file. Several libraries become a single request, but also a single bundle that can no longer be split.dependenciesloads other categories as separate files before the library itself.
With allowProxy, libraries from /apps are served via the /etc.clientlibs path. Visitors reach them without /apps having to be exposed, and the Dispatcher caches them under this path. Adobe now advises against using embed and dependencies unless necessary. They are included in the HTL of the page component or via the policy of the Core Components page component.
One bundle for every page: the result of a grown embed
The AEM project archetype creates a base library that embeds the Core Components and grid libraries, plus a site library from the frontend module. That is sensible for a start. Over the years, components, campaigns and third-party libraries are added, and every new category is attached to the existing bundle, because that requires the least coordination.
The result is a platform on which a detail page with three components loads the code of every component. On simple pages, the shared bundle can be the largest single item. The share of unused code per page type is shown by the Coverage panel in Chrome DevTools. On platforms with a single global bundle, this share is often a major lever for responsiveness.
Bundles per page type: categories by template, not by component
What works is splitting by page type instead of by authoring logic:
- A lean base library for what every page needs, such as grid, navigation and typography.
- A separate category per template or page type with the components that actually appear there. The page component policy defines per template which categories are loaded.
- Rarely used, heavy components such as configurators, maps or video players as a separate library that loads only when the component is on the page.
Splitting slightly increases the number of requests. With HTTP/2 and long cache lifetimes, that is a small price compared with the code that no longer has to run on every page.
Head or end of page: CSS early, JavaScript late
CSS in the head blocks rendering until it has loaded. That is right for the rules of the first view, not for the rest. One large stylesheet for all pages delays the first render and thus Largest Contentful Paint.
JavaScript belongs at the end of the page or is included with defer, unless it is needed for the first view. The policy of the Core Components page component separates libraries for the head from those at the end of the page. Current Core Components also offer attributes such as async and defer for inclusion. Whether they reach the delivered HTML should be checked in the source, because older projects use their own inclusion templates.
Minification and cache lifetime: HTML Library Manager and versioned URLs
The HTML Library Manager combines the files of a category and can minify and compress them. Whether this is active on the publish instance shows in the delivered files. Unminified libraries are not unusual on older platforms.
Long cache lifetimes require a separate URL for every version of a library. AEM as a Cloud Service versions client library URLs with a hash of the content, so browsers and CDN can cache them permanently. AEM 6.5 does not include this versioning; there, it is often handled by the Versioned ClientLibs feature of ACS AEM Commons. Without versioning, only a short cache lifetime remains, and returning visitors download unchanged code again. How /etc.clientlibs is cached in the Dispatcher is described in AEM Dispatcher Cache.
Client libraries and tags: the code that does not come from the project
Besides client libraries, almost every AEM platform loads JavaScript from tag management, such as Adobe Experience Platform Tags (formerly Adobe Launch) with analytics, personalization and consent banner. This part sits in no category and shows up in no project check, yet it affects the same responsiveness. A budget for client libraries without a budget for tags only shifts the problem. With AEM Edge Delivery Services, blocks replace client libraries, and the question of tags and their loading phase stays the same.
Client library requirements in the specification: a budget per page type
- A budget per page type for JavaScript and CSS in kilobytes, compressed transfer size, derived from the measured baseline.
- An upper limit for the share of unused code per page type, measured with the Coverage panel.
- A documented loading mode per category: head, end of page, deferred or on demand.
- Versioned URLs with a long cache lifetime for all libraries.
- Sign-off against the Interaction to Next Paint of real visitors per page type.
The complete wording is in the AEM Performance Requirements Template. How implementation partners and an independent sign-off work together is described in Performance Partner for AEM Agencies. All causes on AEM platforms are put in context under Adobe Experience Manager Performance.
Book a free performance analysis
30 minutes by video. You receive Google's real-user data for your platform compared with two competitors, whether or not a collaboration follows.
Frequently asked questions about AEM client libraries
What is the difference between embed and dependencies in AEM client libraries?
With embed, the code of other categories is merged into the file of the library itself, which saves requests but creates a bundle that cannot be split. With dependencies, other categories are loaded as separate files before the library itself. What matters for performance is that a page loads only the categories its components actually need.
How can JavaScript be loaded late in AEM?
JavaScript that is not needed for the first view belongs at the end of the page or is included with defer. The policy of the Core Components page component separates libraries for head and end of page, and current Core Components also offer async and defer for inclusion. The result should be checked in the delivered HTML.
How large may the client libraries of an AEM page be?
There is no universal limit. The budget is derived per page type from the measured baseline and the target value for Interaction to Next Paint. Besides size, the share executed before the first interaction is what counts.