Expand my Community achievements bar.

Dive into Adobe Summit 2024! Explore curated list of AEM sessions & labs, register, connect with experts, ask questions, engage, and share insights. Don't miss the excitement.
SOLVED

Component specific ClientLibs?

Avatar

Level 2

Hey folks,

I haven't seen a way to do this, but is there a way to load CSS/JS in your <head> conditionally when a specific component is on page?

The use-case is that we have a component with quite a bit of CSS/JS that is ONLY used on a given page or two, so rather than load that CSS/JS everywhere, we only want to load it when that component is on-page.

I'm sure I could query the page I'm on and see if the component is on page, but I was wondering if there's a more performant/built in way to do this.

Thanks!

1 Accepted Solution

Avatar

Correct answer by
Employee Advisor

By default this is not possible. The standard rendering process starts from the top of the page and all generated markup is immediately written to the output buffer, and then sent to the client. That means at the point when you reach your component the header is already written and comitted.

The only way to achieve this is to intercept that behaviour. Use a filter and buffer *all* data written to the output buffer in memory. Then you can render safely all components and if you encounter your special component you can set a flag in the request attributes. The filter can then check for these attributes, change the buffer accordingly and then send everything out.

That approach is a bit risky, because it *can* consume a lot of memory. And it changes the rendering performance and behaviour of your page. But it might be worth a try.

Jörg

View solution in original post

14 Replies

Avatar

Level 4

Why don't you have a specific category for the component clientlibs and include the clientlibs in the component jsp/html file itself?

Avatar

Level 2

I don't think that's exactly what I want because then the JS/CSS would be in the page body where the component is, and not in the <head>.

Avatar

Level 10

There is no way to put a client lib - say in a page header and have it only load if a certain component is dropped onto the page. As Rima suggested - if you only want a clientlib to load for a specific component - add the HTL synatx to the component -- ie:

<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html" data-sly-call="${clientlib.css @ categories='bannerComponent'}"/>

Also - make no difference if call is made in body to head - it still gets loaded either way. In other words - if you only need a specific Clientlib in a component - no advantage to load it within the head section.

Avatar

Community Advisor

I would like to agree with scott and rima. I had tried this few times but could neva figure out a solution . As of now my answer would be no.

Avatar

Correct answer by
Employee Advisor

By default this is not possible. The standard rendering process starts from the top of the page and all generated markup is immediately written to the output buffer, and then sent to the client. That means at the point when you reach your component the header is already written and comitted.

The only way to achieve this is to intercept that behaviour. Use a filter and buffer *all* data written to the output buffer in memory. Then you can render safely all components and if you encounter your special component you can set a flag in the request attributes. The filter can then check for these attributes, change the buffer accordingly and then send everything out.

That approach is a bit risky, because it *can* consume a lot of memory. And it changes the rendering performance and behaviour of your page. But it might be worth a try.

Jörg

Avatar

Level 2

These answers confirmed what I suspected, so I'm trying something a bit different where at the page level, I run a query against the children JCR nodes looking for the component, then if I find it, I add the clientlib on the template...

Thoughts?

Avatar

Level 10

That apprach sounds like it will impact performance. Why  again is adding clientlib to the actual HTML of the component not a good solution for you?

Avatar

Level 2

Though adding component level client library should be a perfect solution for your case, the only problem I am seeing is that the time required to load JS and CSS of that component.

People prefer to load CSS at the head and JS just before end of body because they want to show static stylized content as soon as possible. If this(size of CSS and JS files) is not the problem then you can use component level client library.

Are you using specific editable template(s) for those pages? if yes then will adding client library of that component in page design solve your problem?

Avatar

Level 2

Typically we're trying to load alot of our critical path first and blocking things that aren't styled, but I've been looking into it and maybe this is the route we want to go.

The only thing that is concerning is the JS that would be part of the component, rather than trying to pull everything to the top of the head, Is there a way to defer the JS load for particular components to the bottom of the body?

Avatar

Level 10

" Is there a way to defer the JS load for particular components to the bottom of the body?" - no that is not possible.

Avatar

Level 2

Is this call smart enough to not load the CSS/JS twice if the same component is used twice on the same one page?

Avatar

Employee Advisor

Hi,

you can split the JS of a component into 2 different clientlibs: a "head-CL" and and a "bottom-CL"; the head-CLs are loaded in the head, and the rest at the bottom. Then you can decide for each component which part of the JS is loaded earlier than the rest.

Jörg

Avatar

Level 2

Do you think if this is possible by somehow populating a property on page level with what all components are there in the page (either by traversing nodes in the page or, adding listener on drop to add component category) and dynamically then adding these values in the categories value here in the headlibs/footlibs html?

data-sly-call="${clientlib.all @ categories='properties.componentCategoriesInPage'}"

Avatar

Employee Advisor

To make this work, you would need to traverse the page's resource tree first and scan all included components for the clientlibs they require. That can be a complex task if you need to handle content fragments/experience fragments, iparsys etc as well. If you have an idea, which is both easy to implement and maintain and also does not have a performance impact on page rendering, please let me know :-)

But instead I would recommend to have the JS/CSS of the majority of components being contained in a single clientlib. You are probably delivering more than required on that page, but you reduce the development overhead significantly. And in the end you need to balance these 2: client rendering time vs development effort.