Best practices for Minify or Merge CSS and JS files in a aem-boilerplate-commerce project ? | Community
Skip to main content
elkinomnipro
July 8, 2025
Solved

Best practices for Minify or Merge CSS and JS files in a aem-boilerplate-commerce project ?

  • July 8, 2025
  • 1 reply
  • 1290 views

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!

 

This post is no longer active and is closed to new replies. Need help? Start a new post to ask your question.
Best answer by SantoshSai

@rabiirahmouni 

1. Build Pipeline Integration: Monorepo vs. Split Repos for ClientLibs

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:

    ui.apps/src/main/content/jcr_root/apps/<project>/clientlibs/<bundle>
  • 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.

2. Performance Monitoring of Page-Type Bundling

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

3. Lazy-Loading 3rd-Party Scripts w/ Intersection Observer

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.

4. Scaling Page-Type Bundling with Large Product Catalogs

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

1 reply

SantoshSai
Community Advisor
Community Advisor
July 8, 2025

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.

Santosh Sai
RabiiRahmouni
July 8, 2025

@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.

SantoshSai
Community Advisor
SantoshSaiCommunity AdvisorAccepted solution
Community Advisor
July 8, 2025

@rabiirahmouni 

1. Build Pipeline Integration: Monorepo vs. Split Repos for ClientLibs

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:

    ui.apps/src/main/content/jcr_root/apps/<project>/clientlibs/<bundle>
  • 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.

2. Performance Monitoring of Page-Type Bundling

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

3. Lazy-Loading 3rd-Party Scripts w/ Intersection Observer

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.

4. Scaling Page-Type Bundling with Large Product Catalogs

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

Santosh Sai