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 |