Hi everyone, good day!
What would you recommend as the best approach for minifying and merging files generated and added during the build process ?
As the projects grows, more and more style tags are being injected into the head section, so I think it would be better to merge and/or minify them into a few (or even just one) to improve site performance.
Additionally, do you have any recommendations or best practices for efficiently loading styles and scripts, especially when dealing with third-party providers such as GTM, Zendesk, etc.?
Thanks in advance for your insights!
Solved! Go to Solution.
Views
Replies
Total Likes
Best Practice: Keep the frontend build tooling co-located within the AEM repo, especially for Content and Experience-Driven projects (like AEM Commerce).
Why:
Tighter integration with the ui.apps structure (eg. clientlibs paths)
Easier local dev experience using mvn clean install and npm run build
Simplifies deployment via Cloud Manager or Jenkins pipelines
Alternative (if you're scaling fast):
Use a separate frontend repo (monorepo style or micro-frontend-based)
Output the final /dist or /build assets directly to:
Sync it using a Git submodule, CI/CD artifact copy step, or symbolic linking (npm run build:copy-to-clientlib)
Choose this route if you have independent teams maintaining design systems or shared components across multiple sites.
Absolutely - here’s how we measure performance gains per bundle:
Core Web Vitals (CWV) – Our Go-To Metrics:
LCP (Largest Contentful Paint) -> most sensitive to CSS blocking
FID (First Input Delay) -> influenced by JS bundle execution
CLS (Cumulative Layout Shift) -> mostly layout CSS + async script order
Tools We Use:
Lighthouse CI -> integrate into PR validation
WebPageTest -> simulate 3G/mobile loads
New Relic / Datadog RUM -> enterprise-level Real User Monitoring
Adobe Monitoring (Cloud Manager pipeline also gives some CWV)
Custom window.performance.mark() hooks to measure JS load time for each bundle
Bundle-level Tracking:
We use bundle-specific names (eg. pdp-bundle.js, plp.css) and:
Track in browser DevTools -> see which bundle blocks rendering
Set up performance budgets in Webpack -> alert on bloat
Yes! Intersection Observer (IO) is game-changing for fine-tuned lazy-loading of widgets like chat, reviews, recommendation carousels, etc.
Example: Lazy-load Zendesk on visibility
const loadZendesk = () => {
const script = document.createElement('script');
script.src = 'https://static.zdassets.com/ekr/snippet.js?...';
script.async = true;
document.body.appendChild(script);
};
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadZendesk();
obs.disconnect();
}
});
}, { threshold: 0.25 });
const target = document.getElementById('zendesk-widget-placeholder');
if (target) observer.observe(target);
This keeps the main thread clean during page load and improves TTI/FID scores dramatically.
Yes, it scales extremely well, if you scope bundles smartly.
Strategy:
Bundle per layout complexity, not SKU volume.
eg. PLP = grid + filter UI, PDP = tabs + image zoom.
Reuse shared libraries (react, swiper, etc.) via vendor chunking (eg. vendor.js)
Use code splitting with Webpack dynamic import() for:
Size charts
Reviews
Personalized recommendations
Hi @elkinomnipro,
1. Use Frontend Build Tools (Webpack/Vite) for Optimization
Handle all minification and bundling in your frontend pipeline before deployment:
Bundle and minify your CSS and JS during build time.
Output to AEM clientlibs:
/apps/your-project/clientlibs/site/
├── css.txt → includes minified CSS
├── js.txt → includes minified JS
Reference these in your page component using:
<sly data-sly-use.clientlib="/libs/granite/sightly/templates/clientlib.html"
data-sly-call="${clientlib.all @ categories='your.project.site'}"/>
2. ClientLibs Configuration for Production
In your clientlibs/site/.content.xml:
<jcr:root
jcr:primaryType="cq:ClientLibraryFolder"
categories="[your.project.site]"
allowProxy="{Boolean}true"
versioned="{Boolean}true"
jsProcessor="[default:none,min:gcc]"
cssProcessor="[default:none,min:none]"/>
This ensures:
Minification using Google Closure Compiler (min:gcc)
Versioned assets for better cache control
Delivery via /etc.clientlibs with CDN support
3. Smart Merging Strategy
Instead of creating one large file:
Bundle by page type (e.g., home, PLP, PDP)
Keep third-party scripts separate for lazy loading
Use async/defer for non-critical JS
4. Efficient 3rd-Party Script Loading (GTM, Zendesk, etc.)
Load critical third-party scripts like GTM in <head> with async:
<script async src="https://www.googletagmanager.com/gtm.js?id=GTM-XXXXXX"></script>
Defer others like Zendesk or chat widgets until:
User scrolls
After first interaction
Using requestIdleCallback() or similar
You can also create a separate clientlib (eg. clientlibs/zendesk) and load it only on needed pages.
@SantoshSai Thank you for this comprehensive breakdown! Your approach to frontend optimization in AEM Commerce is exactly what I was looking for.
A few follow-up questions based on your recommendations:
1. **Build Pipeline Integration**: When using Webpack/Vite with AEM clientlibs, do you recommend maintaining the build process within the AEM project structure, or would you suggest a separate frontend repository that outputs to the clientlibs directory during CI/CD?
2. **Performance Monitoring**: For the page-type bundling strategy you mentioned (home, PLP, PDP), how do you typically measure the performance impact? Are you using specific Core Web Vitals metrics or AEM-specific performance tools?
3. **Third-Party Script Management**: Your point about lazy-loading third-party scripts is brilliant. Have you experimented with using Intersection Observer API for more granular control over when these scripts load, especially for below-the-fold widgets?
As someone transitioning to Adobe Commerce development, I'm particularly interested in understanding how these optimization patterns scale with larger product catalogs. Do you find that the page-type bundling approach remains effective for sites with thousands of SKUs?
Thanks again for the detailed technical guidance - this is exactly the kind of enterprise-level thinking I'm working to develop in my Adobe Commerce specialization.
Best Practice: Keep the frontend build tooling co-located within the AEM repo, especially for Content and Experience-Driven projects (like AEM Commerce).
Why:
Tighter integration with the ui.apps structure (eg. clientlibs paths)
Easier local dev experience using mvn clean install and npm run build
Simplifies deployment via Cloud Manager or Jenkins pipelines
Alternative (if you're scaling fast):
Use a separate frontend repo (monorepo style or micro-frontend-based)
Output the final /dist or /build assets directly to:
Sync it using a Git submodule, CI/CD artifact copy step, or symbolic linking (npm run build:copy-to-clientlib)
Choose this route if you have independent teams maintaining design systems or shared components across multiple sites.
Absolutely - here’s how we measure performance gains per bundle:
Core Web Vitals (CWV) – Our Go-To Metrics:
LCP (Largest Contentful Paint) -> most sensitive to CSS blocking
FID (First Input Delay) -> influenced by JS bundle execution
CLS (Cumulative Layout Shift) -> mostly layout CSS + async script order
Tools We Use:
Lighthouse CI -> integrate into PR validation
WebPageTest -> simulate 3G/mobile loads
New Relic / Datadog RUM -> enterprise-level Real User Monitoring
Adobe Monitoring (Cloud Manager pipeline also gives some CWV)
Custom window.performance.mark() hooks to measure JS load time for each bundle
Bundle-level Tracking:
We use bundle-specific names (eg. pdp-bundle.js, plp.css) and:
Track in browser DevTools -> see which bundle blocks rendering
Set up performance budgets in Webpack -> alert on bloat
Yes! Intersection Observer (IO) is game-changing for fine-tuned lazy-loading of widgets like chat, reviews, recommendation carousels, etc.
Example: Lazy-load Zendesk on visibility
const loadZendesk = () => {
const script = document.createElement('script');
script.src = 'https://static.zdassets.com/ekr/snippet.js?...';
script.async = true;
document.body.appendChild(script);
};
const observer = new IntersectionObserver((entries, obs) => {
entries.forEach(entry => {
if (entry.isIntersecting) {
loadZendesk();
obs.disconnect();
}
});
}, { threshold: 0.25 });
const target = document.getElementById('zendesk-widget-placeholder');
if (target) observer.observe(target);
This keeps the main thread clean during page load and improves TTI/FID scores dramatically.
Yes, it scales extremely well, if you scope bundles smartly.
Strategy:
Bundle per layout complexity, not SKU volume.
eg. PLP = grid + filter UI, PDP = tabs + image zoom.
Reuse shared libraries (react, swiper, etc.) via vendor chunking (eg. vendor.js)
Use code splitting with Webpack dynamic import() for:
Size charts
Reviews
Personalized recommendations
Thank you @SantoshSai for the explanation, I'll definitely try the recommendations you provide.
While there are some general ones that apply to any system, there are some AEM-specific ones I should start testing.
I'm coming from Adobe Commerce Luma and am learning a lot from this new path.
Thanks in advance to all the thread participants for their comments and/or resources they can share on the topic. In general, any feedback regarding web performance in AEM will be very helpful.
Cheers !
Hi @SantoshSai ,
Just to clarify, question is specifically related to a project based on Adobe Edge Delivery Services, using the aem-boilerplate-commerce starter. In this architecture, we are not using Maven archetypes or traditional AEM clientlibs, so the recommendations about /apps/.../clientlibs and .content.xml don’t apply in this context.
Since all styles and scripts are injected during the build process and pushed to the Git repository, @elkinomnipro is looking for best practices around optimizing CSS and JS inside this Git-based setup, such as:
How to bundle and minify styles/scripts before they get injected into the final HTML output (e.g., in blocks, scripts, styles folders).
Recommendations for modular loading, especially as the number of blocks increases.
Efficient handling of third-party scripts (like GTM or Zendesk), possibly through lazy loading or deferred injection strategies tailored for EDS.
If anyone in the EDS community has tackled similar challenges in aem-boilerplate or similar setups, We’d love to hear how you approached this.
Thanks again!
Views
Replies
Total Likes
Views
Likes
Replies
Views
Likes
Replies
Views
Likes
Replies