General Guide: See Global LCP Guide for universal concepts and fixes.
What is LCP?
Largest Contentful Paint measures when the largest content element becomes visible. Google recommends LCP under 2.5 seconds. Magnolia CMS is a Java-based enterprise CMS using FreeMarker templates and a JCR content repository. LCP depends on Magnolia's cache module, DAM renditions, and template rendering.
Magnolia-Specific LCP Causes
- Cache module not configured -- Magnolia's cache module stores rendered pages but needs explicit configuration per page template
- DAM renditions not pre-generated -- Magnolia's imaging module generates renditions on-demand, adding processing time on first request
- FreeMarker template complexity -- nested area/component rendering with multiple JCR queries per component
- Component rendering overhead -- each component in a page area triggers its own FreeMarker template and content query
- Headless API latency -- Magnolia's Content Delivery API adds serialization overhead for headless implementations
Fixes
1. Configure Magnolia Cache
Configure caching in your Magnolia configuration:
# /modules/cache/config.yaml
cacheFactory:
cacheModule:
name: magnolia-cache
configuration:
defaultCacheConfiguration:
maxEntriesLocalHeap: 10000
timeToLiveSeconds: 3600
timeToIdleSeconds: 1800
# Enable page caching for specific templates
pageCache:
enabled: true
templates:
- pages/home
- pages/article
- pages/landing
2. Pre-Generate DAM Renditions
Configure image renditions and warm the cache:
# /modules/dam/config/renditions.yaml
renditions:
hero:
width: 1920
height: 600
quality: 80
thumbnail:
width: 400
height: 300
quality: 75
mobile:
width: 640
height: 400
quality: 80
Use renditions in FreeMarker templates:
<#-- In your component template -->
<#assign heroAsset = damfn.getAsset(content.heroImage)>
<#if heroAsset?has_content>
<img
src="${damfn.getRendition(heroAsset, 'hero').getLink()}"
srcset="${damfn.getRendition(heroAsset, 'mobile').getLink()} 640w,
${damfn.getRendition(heroAsset, 'hero').getLink()} 1920w"
sizes="100vw"
width="1920" height="600"
alt="${heroAsset.getCaption()!''}"
loading="eager"
fetchpriority="high"
>
</#if>
3. Optimize Component Rendering
Reduce JCR queries per component:
<#-- BAD: Multiple content queries in component template -->
<#assign relatedArticles = cmsfn.children(cmsfn.page(content), "mgnl:page")>
<#list relatedArticles as article>
<#assign author = cmsfn.contentByReference(article.author)>
</#list>
<#-- GOOD: Use content delivery API with projection -->
<#assign items = cmsfn.children(content, "mgnl:component")?sort_by("order")>
4. Preload LCP Assets
<#-- In your page template head section -->
<#if cmsfn.isCurrentPage()>
<link rel="preload" as="image"
href="${damfn.getRendition(heroAsset, 'hero').getLink()}" />
</#if>
<#-- Preconnect to DAM CDN -->
<link rel="preconnect" href="${cmsfn.link(cmsfn.page(content), 'dam')}" />
5. Enable Content Delivery API Caching
For headless implementations:
# /modules/content-delivery/config.yaml
deliveryEndpoint:
cache:
enabled: true
maxAge: 300
staleWhileRevalidate: 3600
Measuring LCP on Magnolia
- Magnolia AdminCentral > Monitoring -- check page render times and cache hit rates
- JMX metrics -- monitor cache efficiency through JMX beans
- PageSpeed Insights -- test rendered pages (not AdminCentral URLs)
- Test page types: landing pages with many components, article pages, and listing pages
Analytics Script Impact
Magnolia analytics is typically added via FreeMarker templates or the analytics connector module. Ensure all scripts use async/defer and are placed before </body>.