General Guide: See Global LCP Guide for universal concepts and fixes.
AEM-Specific LCP Causes
1. Heavy Client Libraries
Blocking clientlibs delay page render:
<!-- Problem: Blocking CSS in head -->
<sly data-sly-call="${clientlib.all @ categories='mysite.all'}"/>
2. Unoptimized DAM Assets
Large images from DAM without optimization:
<!-- Problem: Full-size DAM image -->
<img src="/content/dam/mysite/hero.jpg"/>
3. Dispatcher Cache Misses
Cold cache or frequent invalidation:
MISS → Origin fetch → Slow response
4. Component Render Time
Complex HTL logic or slow Sling Models:
// Slow: Heavy query in model
public List<Page> getRelatedPages() {
return pageManager.search(new Query()); // Expensive!
}
AEM-Specific Fixes
Fix 1: Optimize Client Library Loading
Split critical and non-critical CSS:
<!-- Critical CSS inline -->
<style data-sly-use.critical="${'com.mysite.CriticalCSSModel'}">
${critical.css @ context='unsafe'}
</style>
<!-- Non-critical async -->
<link rel="preload" as="style"
href="/etc.clientlibs/mysite/clientlibs/site.css"
Use async for JavaScript:
<!-- clientlib definition -->
<jcr:root xmlns:jcr="http://www.jcp.org/jcr/1.0"
jcr:primaryType="cq:ClientLibraryFolder"
categories="[mysite.scripts]"
jsProcessor="[default:none,min:none]"
async="{Boolean}true"/>
Fix 2: Use Dynamic Media or Asset Compute
Enable Dynamic Media for responsive images:
<sly data-sly-use.dm="${'com.adobe.cq.dam.dm.api.DMImageProfileService'}">
<img src="${asset.path @ selectors='width_800', extension='webp'}"
srcset="${asset.path @ selectors='width_400'}?wid=400 400w,
${asset.path @ selectors='width_800'}?wid=800 800w"
sizes="(max-width: 600px) 400px, 800px"
loading="lazy"
width="800"
height="600"
alt="${asset.title}">
</sly>
Or use Asset Compute:
<!-- Web-optimized delivery -->
<img src="${asset.path}.transform/width-800/format-webp/quality-85.webp"/>
Fix 3: Optimize Dispatcher Caching
Configure aggressive caching:
dispatcher.any
/cache {
/rules {
/0000 { /glob "*" /type "allow" }
}
/headers {
"Cache-Control"
"Expires"
}
/gracePeriod "300" # Serve stale while revalidating
}
Add preload hints:
<sly data-sly-use.page="${'com.mysite.core.models.PageModel'}">
<link rel="preload"
href="${page.heroImage}"
as="image"
type="image/webp">
</sly>
Fix 4: Optimize Sling Models
Use lazy loading and caching:
@Model(adaptables = SlingHttpServletRequest.class,
cache = true)
public class PageModel {
private List<Page> relatedPages;
// Lazy initialization
public List<Page> getRelatedPages() {
if (relatedPages == null) {
relatedPages = computeRelatedPages();
}
return relatedPages;
}
@PostConstruct
protected void init() {
// Only initialize critical data here
}
}
Fix 5: Use Core Components
Core Components are optimized for performance:
<!-- Use Core Image Component -->
<div data-sly-resource="${'image' @ resourceType='core/wcm/components/image/v3/image'}">
</div>
Core Image automatically:
- Generates responsive srcset
- Lazy loads below-fold images
- Applies web-optimized renditions
Fix 6: Preconnect to External Resources
Add preconnect for third-party resources:
<sly data-sly-test="${page.hasAnalytics}">
<link rel="preconnect" href="https://www.googletagmanager.com">
<link rel="preconnect" href="https://www.google-analytics.com">
<link rel="dns-prefetch" href="https://www.googletagmanager.com">
</sly>
Monitoring LCP
Track with Web Vitals:
// Client library: mysite.webvitals
import {onLCP} from 'web-vitals';
onLCP(function(metric) {
window.dataLayer = window.dataLayer || [];
window.dataLayer.push({
'event': 'web_vitals',
'vital_name': 'LCP',
'vital_value': metric.value,
'vital_rating': metric.rating,
'vital_element': metric.entries[0]?.element?.tagName
});
});
LCP Targets
| Rating | LCP Value |
|---|---|
| Good | ≤ 2.5s |
| Needs Improvement | 2.5s - 4.0s |
| Poor | > 4.0s |