Why Largest Contentful Paint Matters More Than Ever
Largest Contentful Paint (LCP) measures how long it takes for the biggest visible element on your page to fully render. It could be a hero image, a headline, a video poster, or a large block of text. Google considers a good LCP score to be 2.5 seconds or less. Anything above 4 seconds is rated poor.
As a Core Web Vital, LCP directly influences your search rankings, user experience, and conversion rates. If your pages feel sluggish, there is a strong chance your LCP score is the culprit. In this guide, we will walk through exactly how to diagnose and improve Largest Contentful Paint step by step, whether you are a developer tuning an Express.js application or a website owner trying to understand why your site feels slow.
Step 1: Identify Your LCP Element
Before you fix anything, you need to know what your LCP element actually is. Different pages may have different LCP elements. Here is how to find yours:
- Open Chrome DevTools and go to the Performance tab.
- Record a page load and look for the “LCP” marker in the timeline.
- Alternatively, run a Lighthouse audit. The report will explicitly tell you which element is your Largest Contentful Paint element.
- Use PageSpeed Insights or the Chrome User Experience Report (CrUX) for real-world field data.
Common LCP elements include:
- Hero images or background images
<img>tags above the fold<video>poster images- Large heading or paragraph text blocks
- SVGs or canvas elements
Pro tip: If your LCP element is text-based, your optimization path is significantly simpler. Text renders as soon as the CSS and fonts are ready. If it is an image, you have more work to do.
Step 2: Reduce Server Response Time (TTFB)
Everything starts with how fast your server responds. Time to First Byte (TTFB) is the foundation of LCP. If your server takes 1.5 seconds to respond, you have already burned more than half your LCP budget before the browser even starts rendering.
How to Optimize TTFB
| Strategy | What It Does | Expected Impact |
|---|---|---|
| Use a CDN | Serves content from edge servers geographically close to users | High |
| Enable server-side caching | Avoids regenerating pages on every request | High |
| Upgrade hosting | Better CPU/memory means faster response times | Medium to High |
| Optimize database queries | Reduces backend processing time | Medium |
| Use stale-while-revalidate | Serves cached content while refreshing in the background | Medium |
If you are running an Express.js server, consider adding response caching middleware and ensuring your routes do not perform expensive synchronous operations during the request lifecycle.
Step 3: Eliminate Render-Blocking Resources
Render-blocking CSS and JavaScript prevent the browser from painting content until they are fully downloaded and parsed. This is one of the most common reasons for a poor LCP score.
CSS Optimization
- Inline critical CSS: Extract the CSS needed for above-the-fold content and place it directly in a
<style>tag in the<head>. - Defer non-critical CSS: Load the rest of your stylesheets asynchronously using
rel="preload"with anonloadhandler or themediaattribute trick. - Remove unused CSS: Tools like PurgeCSS can strip out styles your page never uses. On large projects, unused CSS can account for 80% or more of your stylesheet.
- Minify and compress: Use tools like cssnano for minification and ensure Brotli or Gzip compression is enabled on your server.
JavaScript Optimization
- Add
deferorasyncattributes to script tags that are not essential for initial rendering. - Move third-party scripts (analytics, chat widgets, ad tags) below the fold or load them after the LCP element has rendered.
- Consider breaking your JavaScript bundle into smaller chunks and loading only what the current page needs.
Step 4: Optimize Images (The Biggest LCP Win)
Images are the LCP element on the majority of web pages. Optimizing them is often the single most impactful thing you can do to improve Largest Contentful Paint.
Image Format
Use modern image formats that offer better compression ratios than JPEG and PNG:
- WebP: Widely supported across all modern browsers. Typically 25-35% smaller than JPEG at comparable quality.
- AVIF: Even better compression than WebP, with growing browser support. In 2026, AVIF support is robust enough for production use with a JPEG fallback.
Use the <picture> element to serve the best format each browser supports:
<picture>
<source srcset="hero.avif" type="image/avif">
<source srcset="hero.webp" type="image/webp">
<img src="hero.jpg" alt="descriptive alt text" width="1200" height="600">
</picture>
Responsive Images
Do not serve a 2400px wide image to a phone with a 400px viewport. Use the srcset and sizes attributes to let the browser pick the right size:
<img
src="hero-800.jpg"
srcset="hero-400.jpg 400w, hero-800.jpg 800w, hero-1200.jpg 1200w, hero-1600.jpg 1600w"
sizes="(max-width: 600px) 100vw, (max-width: 1200px) 80vw, 1200px"
alt="descriptive alt text"
width="1200"
height="600"
>
Image Loading Priorities
This is critical and often overlooked:
- Never lazy-load your LCP image. Lazy loading tells the browser to deprioritize the image, which directly delays LCP.
- Use
fetchpriority="high"on the LCP image to tell the browser to download it as early as possible. - Preload the LCP image if it is referenced in CSS (like a background image) rather than an
<img>tag:
<link rel="preload" as="image" href="hero.webp" type="image/webp">
Additional Image Tips
- Always specify
widthandheightattributes to prevent layout shifts. - Use an image CDN (like Cloudinary, imgix, or Cloudflare Images) that can dynamically resize, compress, and convert images on the fly.
- Compress images before upload. Tools like Squoosh, Sharp, or ImageOptim can dramatically reduce file sizes.
Step 5: Optimize Font Loading
If your LCP element is text, web fonts can be a hidden bottleneck. By default, many browsers hide text until custom fonts load (a behavior called FOIT, or Flash of Invisible Text). This means your largest text block might be invisible for hundreds of milliseconds or more.
Font Loading Strategies
- Use
font-display: swap: This tells the browser to show text immediately using a fallback font, then swap in the custom font once it loads. Add it to your@font-facedeclarations:@font-face { font-family: 'CustomFont'; src: url('/fonts/custom.woff2') format('woff2'); font-display: swap; } - Preload your most important font files:
<link rel="preload" as="font" href="/fonts/custom.woff2" type="font/woff2" crossorigin> - Use WOFF2 format exclusively: It offers the best compression and has universal browser support in 2026.
- Self-host your fonts instead of loading from Google Fonts or other third-party services. This removes an extra DNS lookup and connection setup.
- Subset your fonts: If you only need Latin characters, do not ship Cyrillic, Greek, and CJK glyphs. Tools like
pyftsubsetorglyphhangercan reduce font file sizes by 50-90%.
Step 6: Preload and Prioritize Critical Resources
The browser discovers resources as it parses your HTML. Some resources, especially those referenced in CSS or JavaScript, are discovered late. You can fix this with preloading.
What to Preload
- Your LCP image (especially if it is a CSS background image)
- Critical fonts
- Any resource that the browser cannot discover from the initial HTML alone
What NOT to Preload
- Everything. Preloading too many resources dilutes the benefit and can actually slow things down by competing for bandwidth.
- Resources that are already discoverable in the HTML (like an
<img>tag in the<head>).
Additionally, use the 103 Early Hints HTTP response to let the browser start loading critical resources even before your server has finished generating the full HTML response. This is especially powerful for Express.js applications with dynamic content.
Step 7: Minimize Client-Side Rendering Delays
If your application relies heavily on JavaScript to render content (like a single-page application built with React, Vue, or Angular), your LCP will suffer because the browser must:
- Download the HTML shell
- Download the JavaScript bundle
- Parse and execute the JavaScript
- Fetch data from APIs
- Finally render the content
Each of these steps adds time before the LCP element appears.
Solutions for Client-Side Rendering
- Server-Side Rendering (SSR): Render the initial HTML on the server so the browser receives ready-to-display content.
- Static Site Generation (SSG): Pre-render pages at build time for even faster delivery.
- Streaming SSR: Send HTML to the browser in chunks as it is generated, allowing the browser to start rendering sooner.
- Hybrid approaches: Render critical above-the-fold content on the server while hydrating interactive elements on the client.
Step 8: Optimize Your CSS Delivery Architecture
Beyond eliminating render-blocking CSS, consider the overall architecture of your stylesheet delivery:
- Avoid CSS @import chains: Each
@importcreates a sequential request that the browser cannot parallelize. Use<link>tags instead. - Reduce CSS specificity complexity: Deeply nested selectors take longer to evaluate. Simpler selectors mean faster style computation.
- Split CSS per page or route: Do not force users to download CSS for your checkout page when they are reading a blog post.
Step 9: Use a Content Delivery Network (CDN)
A CDN reduces the physical distance between your server and your users. For global audiences, this alone can cut hundreds of milliseconds from your LCP score.
Key CDN best practices for LCP:
- Cache HTML pages at the edge when possible (especially for static or semi-static content).
- Use the CDN for all static assets: images, fonts, CSS, and JavaScript.
- Enable Brotli compression at the CDN level.
- Configure appropriate
Cache-Controlheaders so returning visitors get near-instant loads.
Step 10: Monitor and Measure Continuously
LCP is not a set-it-and-forget-it metric. New features, third-party scripts, content changes, and infrastructure updates can all cause regressions.
Recommended Monitoring Tools
| Tool | Type | Best For |
|---|---|---|
| PageSpeed Insights | Lab + Field | Quick checks with real-user data from CrUX |
| Lighthouse (DevTools) | Lab | Detailed audits during development |
| Chrome UX Report (CrUX) | Field | Real-world performance data over 28 days |
| Web Vitals JS Library | Field (RUM) | Custom real-user monitoring in your own analytics |
| WebPageTest | Lab | Filmstrip views, waterfall analysis, multi-step testing |
Set up automated performance budgets in your CI/CD pipeline. If a pull request pushes LCP past your threshold, block the deployment until it is fixed.
LCP Optimization Checklist
Here is a quick-reference checklist you can work through for any page:
- ☐ Identify the LCP element using Lighthouse or DevTools
- ☐ Ensure TTFB is under 800ms
- ☐ Inline critical CSS and defer the rest
- ☐ Remove or defer render-blocking JavaScript
- ☐ Serve images in WebP or AVIF format
- ☐ Use responsive images with
srcsetandsizes - ☐ Do NOT lazy-load the LCP image
- ☐ Add
fetchpriority="high"to the LCP image - ☐ Preload LCP images referenced in CSS
- ☐ Use
font-display: swapon all custom fonts - ☐ Preload critical font files
- ☐ Self-host and subset fonts
- ☐ Use a CDN for all static assets
- ☐ Enable Brotli or Gzip compression
- ☐ Implement SSR or SSG if using a JavaScript framework
- ☐ Set up continuous monitoring
Frequently Asked Questions
What is a good LCP score?
Google considers an LCP of 2.5 seconds or less as good. Scores between 2.5 and 4 seconds need improvement, and anything above 4 seconds is rated poor. Aim for under 2 seconds for the best user experience.
What is the most common cause of poor LCP?
Unoptimized images are the most frequent cause. Large hero images served in outdated formats without proper sizing or priority hints account for the majority of LCP issues we see.
Does LCP affect SEO rankings?
Yes. LCP is one of the three Core Web Vitals that Google uses as a ranking signal. While content relevance remains the primary ranking factor, LCP can be the tiebreaker between competing pages.
Should I lazy-load images to improve LCP?
Lazy-load images that are below the fold, but never lazy-load your LCP image. Lazy loading delays the image request until the browser determines it is near the viewport, which directly harms LCP.
How is LCP different from First Contentful Paint (FCP)?
FCP measures when the first piece of content appears on screen (even a small loading spinner). LCP measures when the largest content element finishes rendering. FCP tells you the page has started loading; LCP tells you the main content is visible and usable.
Can third-party scripts hurt my LCP?
Absolutely. Third-party scripts compete for bandwidth and main thread processing time. Chat widgets, analytics libraries, A/B testing scripts, and ad tags can all delay LCP. Load them asynchronously and after your critical content has rendered.
How do I improve LCP on mobile specifically?
Mobile devices have slower processors and often slower network connections. Focus on reducing total page weight, serving appropriately sized images for smaller viewports, minimizing JavaScript execution, and ensuring your server response time is fast regardless of network conditions. Using a CDN is especially impactful for mobile users on cellular networks.
How often should I check my LCP score?
Monitor LCP continuously using real-user monitoring (RUM). At minimum, check after every deployment, content update, or change to third-party scripts. Performance regressions can happen silently, so automated monitoring is strongly recommended.
