Fix SAP Commerce Cloud LCP Issues | OpsBlu Docs

Fix SAP Commerce Cloud LCP Issues

Reduce SAP Commerce LCP by trimming Spartacus bundle size, enabling SSR with Angular Universal, and optimizing media CDN delivery.

General Guide: See Global LCP Guide for universal concepts and fixes.

SAP Commerce-Specific LCP Causes

1. Spartacus Bundle Size

Large JavaScript bundles delay hydration:

Problem: Main bundle exceeds 500KB
Impact: Delays interactive rendering

2. OCC API Response Time

Slow Commerce API responses block content:

// Slow: Multiple sequential API calls
this.productService.get(code).pipe(
  switchMap(product => this.stockService.get(code))
).subscribe();

3. Unoptimized Product Images

Large product images without CDN optimization:

<!-- Problem: Full-size images loaded -->
<img [src]="product.images.PRIMARY.url" />

4. SSR Hydration Delay

Angular Universal hydration blocking interactivity:

SSR HTML → Download JS → Hydrate → Interactive
   └── LCP element visible but page frozen

SAP Commerce-Specific Fixes

Fix 1: Optimize Spartacus Bundles

Enable lazy loading for feature modules:

src/app/spartacus/spartacus-features.module.ts

import { NgModule } from '@angular/core';
import {
  ProductDetailsPageModule,
  SearchResultsPageModule
} from '@spartacus/storefront';

@NgModule({
  imports: [
    // Lazy load heavy modules
    ProductDetailsPageModule,
    SearchResultsPageModule
  ]
})
export class SpartacusFeaturesModule {}

Configure Angular build optimization:

angular.json

{
  "build": {
    "configurations": {
      "production": {
        "optimization": true,
        "buildOptimizer": true,
        "sourceMap": false,
        "namedChunks": false,
        "extractLicenses": true,
        "vendorChunk": false,
        "budgets": [
          {
            "type": "initial",
            "maximumWarning": "500kb",
            "maximumError": "1mb"
          }
        ]
      }
    }
  }
}

Fix 2: Optimize OCC API Calls

Use parallel requests and caching:

// Better: Parallel API calls with caching
import { forkJoin } from 'rxjs';
import { shareReplay } from 'rxjs/operators';

const product$ = this.productService.get(code).pipe(
  shareReplay(1)
);

const stock$ = this.stockService.get(code).pipe(
  shareReplay(1)
);

forkJoin([product$, stock$]).subscribe(([product, stock]) => {
  // Both available simultaneously
});

Configure OCC caching in backend:

# hybris/config/local.properties
occ.cache.enabled=true
occ.cache.time.to.live=300

Fix 3: Implement Image Optimization

Use SmartEdit image optimization or CDN:

<cx-media
  [container]="product.images"
  format="product"
  [loading]="index === 0 ? 'eager' : 'lazy'"
  width="400"
  height="400">
</cx-media>

Configure image CDN transformation:

// Custom media service
@Injectable()
export class OptimizedMediaService extends MediaService {
  getMedia(container: any, format?: string) {
    const media = super.getMedia(container, format);

    if (media?.url) {
      // Add CDN transformation params
      media.url = `${media.url}?w=400&q=80&f=auto`;
    }

    return media;
  }
}

Fix 4: Optimize SSR Hydration

Transfer critical state to avoid re-fetching:

// app.server.module.ts
import { TransferState, StateKey, makeStateKey } from '@angular/platform-browser';

@Injectable()
export class ServerTransferStateService {
  constructor(
    private transferState: TransferState,
    @Inject(PLATFORM_ID) private platformId: Object
  ) {}

  setKey<T>(key: string, data: T): void {
    if (isPlatformServer(this.platformId)) {
      this.transferState.set(makeStateKey<T>(key), data);
    }
  }

  getKey<T>(key: string): T | null {
    if (isPlatformBrowser(this.platformId)) {
      const stateKey = makeStateKey<T>(key);
      const data = this.transferState.get(stateKey, null);
      this.transferState.remove(stateKey);
      return data;
    }
    return null;
  }
}

Fix 5: Preload Critical Resources

Add preload hints in SSR:

// server.ts
app.get('*', (req, res) => {
  res.render('index', { req }, (err, html) => {
    // Add preload hints
    html = html.replace(
      '</head>',
      `<link rel="preconnect" href="https://api.your-commerce.com">
       <link rel="preload" as="image" href="/assets/hero.webp">
       </head>`
    );
    res.send(html);
  });
});

Monitoring LCP

// Track LCP with web-vitals
import { onLCP } from 'web-vitals';

if (isPlatformBrowser(this.platformId)) {
  onLCP(metric => {
    window.dataLayer?.push({
      event: 'web_vitals',
      vital_name: 'LCP',
      vital_value: metric.value,
      vital_rating: metric.rating
    });
  });
}

LCP Targets

Rating LCP Value
Good ≤ 2.5s
Needs Improvement 2.5s - 4.0s
Poor > 4.0s

Next Steps