I know, I know...I was meant to be back in September...and yet, so much water has ran under that bridge. I won't give you explanations on how busy I've been, I will just continue with this educational series on AEM and frontend.
The clientlibs
If you got here from google or another link, I advise you read the series from the beginning. In this post, we will learn what clientlibs are.
Clientlibs is short for client-side libraries, the pattern used in AEM to manage and ship frontend code to the browser. To better understand it, you should start by reading the official documentation
Let's suppose you have already implemented your frontend build system. Let's suppose it is with webpack and you have javascript bundles, and css bundles. Let's forget about components for now. This code could be for a component, or a module, or even several of them. The important thing is, for all the code that you want to ship together, you must create a clientlib node, by creating a folder with a .context.xml file, that looks like this.
As you can see, the important part is that you have declared that node to be of
jcr:primaryType="cq:ClientLibraryFolder"
The other important thing is that you should assign it to a category, like this:
categories="[the.category.name.you.wish]"
I know you're wondering why it is categories and not ONE category, and why it accepts an array, instead of a single definition. This is because, in essence, you're not giving a name to it, but declaring it as part of a category of name [the.category.name.you.wish]. And you can assign the code in your clientlib, to multiple categories.
Basically, when a clientlib code is 'put together' under the hood, it creates a single file with everything that is assigned to a category, one for javascript, and another one for CSS.
What should I call my category?
Good question. You should follow a certain convention, but that convention is open. I could suggest you form your category by concatenating the path in your folder structure, including your project-id. If you do that, you can reproduce category conventions across many projects, and even create cross dependencies without issues.
Other properties
There are other very important properties to clientlibs.
- dependencies (string) multiple
- embed (string) multiple
- allowProxy (boolean)
and you can see them being used, in this gist.
Now let's explain them
Dependencies
Like with categories, it takes an array of strings, defining the name of other categories that should be loaded, before yours. There is a mechanism in the backend (in the background) that will make sure those categories, with all the code they have, are loaded before this one.
Embed
You can also embed scripts that are necessary for your clientlibs to work. Unlike dependencies, which trigger an independent request, embedded code is concatenated or aggregated in the same file.
You have to be careful with embeds having dependencies of their own, because clientlibs embeds are transitive. This means that, if you embed a clientlib category that by extension has a declared dependency, that dependency WON'T BE LOADED.
allowProxy
Allow proxy is a boolean that makes a category located at the apps/ folder (which is private) made public when hitting
etc.category-name
via a proxy servlet. Basically, if you don't set it to true, and your code is under apps/, your public site will not be able to download it.
The DOTtxt (.txt) files
Now you know how to create a clientlibs folder, you will want to define what code that category is including. For that, you need to import it to using the .txt files.
You have two. 'js.txt' and 'css.txt', one for your javascript and one for your styles.
In our multi-tenant example, we create a clientlib category per component, since we're on http/2 and are able to bundle and request each component's code independently. For that, out clientlibs generation mechanisms, loads, transpiles and/or preprocesses the javascript and styles, and puts the bundles in the clientlibs folder it was instructed to. So we can just declare the name of the bundle in our corresponding .txt file, and that will make sure the code is loaded.
However, let's suppose the bundle was in a different directory. In order to include it, you need to define the base and import it.
Including the code in your page
Now, this is the more 'on a per case basis' method. In my current project, we have a very complex mechanism under the hood. A sling model that basically will, via a template, request the category associated with the component name (since we have conventionally defined all categories are formed from the folder path to the components).
But you can do it manually too, for example, like this (I added all examples to a single gist for the sake of brevity).
And that's it! That will basically include the '< script >' tag with the right src= for js, and the '< link rel... >', for the CSS! And you can do it anywhere on your page!
Last pro tip!
If you're requesting your clientlibs per component, you should make sure you're not duplicating requests. You should have a debouncing mechanism or potentially a good check in the backend, to make sure not to exhaust your performance with multiple requests to a single item!
Now you know all the basic things about clientlibs you should know, to work as a frontend developer with AEM. See you in the next post!
Top comments (2)
Hi Natalia! Thanks for the 4th part.
I wonder how do you handle lazy loading with clientlibs. I've been struggling with that.
Despite you can import the clientlib from the component's htl, that leads to duplicate imports.
Is this the goal of the complex Sling Model that you mention? I mean import only the clientlibs needed by the components in the page.
Super good question! Next post on that!