To improve LCP in WordPress, find the LCP element on each failing template, then shorten each part of its timeline: server response, the delay before the browser requests the resource, the download and the render. On most WordPress sites that means a working page cache, a hero or featured image that is not lazy-loaded and carries fetchpriority="high", correct srcset and sizes so phones get a phone-sized file, and less render-blocking CSS and JavaScript from the theme and plugins. Google rates LCP as good at 2.5 seconds or less at the 75th percentile of field data.
Largest Contentful Paint is one of the three Core Web Vitals, and on WordPress it is the one I usually start with, because its biggest causes sit in the server and the theme, where one fix helps every page. If you need the full picture first, read the complete guide to Core Web Vitals for WordPress.
What is the LCP element on a WordPress page?
LCP is the render time of the largest image or text block visible in the viewport. The browser considers img elements, image elements inside SVG, video poster images, elements with a CSS background image loaded through url(), and block-level text such as headings and paragraphs.
The element changes by template and by device. On a blog post it is usually the featured image. On a home page it is the hero image or the main heading. On a WooCommerce product page it is the main product image. On mobile, a heading can be the LCP while on desktop the same page uses an image. So “fix LCP” really means “fix LCP on this template, on this device”.
To find it:
- PageSpeed Insights shows the LCP element in the Lighthouse diagnostics for that run.
- The Performance panel in Chrome DevTools marks the LCP and shows the element.
- The attribution build of the web-vitals library reports the LCP element from real visits, which is the version I trust most.
How is LCP broken down?
LCP time is the sum of four parts. Looking at them separately tells you where to work, instead of guessing.
| Part | What it covers | Typical WordPress cause |
|---|---|---|
| Time to First Byte | From navigation start until the first byte of HTML | No page cache, cache bypass, slow PHP or database |
| Resource load delay | From first byte until the browser starts downloading the LCP image | Lazy loading, CSS background images, sliders built by JavaScript |
| Resource load duration | The download of the LCP resource | Oversized images, no modern formats, slow image host |
| Element render delay | From download finished until the element paints | Render-blocking CSS and JavaScript, font loading for text LCP |
web.dev’s guide to optimizing LCP recommends keeping load delay and render delay small, so most of the LCP time goes to the server response and the download itself. On WordPress sites with poor LCP I often find the opposite: the image is small, but the browser only learns about it late, or it waits behind a stack of plugin CSS.
What are the most common causes of slow LCP in WordPress?
| Cause | Fix |
|---|---|
| Uncached HTML, slow server | Page cache, persistent object cache, current PHP with OPcache. See the TTFB guide below |
| LCP image lazy-loaded by a plugin, theme or builder | Exclude the first images from lazy loading. Let core decide, or set loading="eager" |
| Hero set as a CSS background | Use an img element in the HTML, or preload the image |
| First slide built by a slider script | Render the first slide in server HTML, or drop the slider for a static hero |
| Image much larger than its display size | Registered image sizes, correct sizes, WebP or AVIF |
| Render-blocking CSS and JavaScript | Defer scripts, remove unused plugin assets per template, inline critical CSS |
| Text LCP waiting for a web font | Self-host the font, preload it, use a suitable font-display |
The first row deserves its own article because TTFB is the floor under every other part. If your HTML takes 1.5 seconds to arrive, you have one second left for everything else. Read how to reduce TTFB in WordPress before you touch images.
How do I make WordPress prioritize the LCP image?
WordPress core already does part of the work. Since WordPress 6.3 it adds fetchpriority="high" to the image it considers most likely to be the LCP, usually the featured image or the first large image in the content, and it avoids adding loading="lazy" to images near the top of the page. The problem is everything that runs around core. Lazy-loading plugins that add loading="lazy" or swap src for data-src on every image. Builders with a global lazy-load setting. Theme templates that print images with hard-coded attributes.
Check the HTML source of a failing page, not the rendered DOM. The LCP image should be in the HTML, without lazy loading, with fetchpriority="high". If your theme outputs the hero itself, pass the attributes explicitly:
wp_get_attachment_image( $id, 'full', false, array( 'loading' => 'eager', 'fetchpriority' => 'high' ) );
When the LCP image has to stay a CSS background, preload it in the head with <link rel="preload" as="image" href="..." fetchpriority="high">, adding imagesrcset and imagesizes if you serve responsive versions. Use high priority on one image per page. If everything is high priority, nothing is.
How do image sizes and formats affect LCP?
WordPress generates several sizes for every upload and outputs srcset and sizes for images in content. The browser picks a file from srcset based on sizes, so a wrong sizes value makes a phone download the desktop file. Full-width heroes often need sizes="100vw", and themes that print heroes with custom code often omit sizes entirely. Register sizes that match your layout with add_image_size() instead of serving the original upload.
Formats help the download part. WordPress supports uploading WebP since version 5.8 and AVIF since 6.5, as long as the server’s image library supports them. Converting a 600 KB JPEG hero to a well-compressed modern format is one of the cheapest LCP wins there is. Keep quality settings sane, because an LCP image that looks bad will get replaced by a designer within a month.
Why do CSS and JavaScript delay LCP?
Stylesheets in the head block rendering. A WordPress page loads the theme stylesheet plus the styles of every plugin that enqueues globally, so the browser can have the LCP image downloaded and still wait to paint it. Block themes load styles only for the blocks used on each page, and classic themes can opt in to the same behavior with the should_load_separate_core_block_assets filter.
For scripts, WordPress 6.3 added loading strategies: register a script with 'strategy' => 'defer' and core prints it with defer, keeping it out of the critical path. For plugins that don’t use it, dequeue their assets on templates that don’t need them with wp_dequeue_script() and wp_dequeue_style(). Critical CSS from a caching plugin can also help render delay, but test it carefully, because a wrong critical CSS file creates the layout shifts I describe in how to reduce CLS in WordPress.
How do I verify an LCP fix?
Check the lab first: a Lighthouse run and a DevTools trace should show the LCP image requested early and the four parts shrinking. Then check real visits. Real user monitoring with the web-vitals library shows the change within days. CrUX, the data behind PageSpeed Insights and Search Console, needs its full 28-day window before the p75 reflects the fix. Don’t trust a lab improvement until the field confirms it. I explain why in Core Web Vitals field data vs lab data. If the server is your bottleneck, choosing the right cache layer usually comes first.
Need help with LCP on your WordPress site?
If your LCP still fails after the obvious fixes, the cause is usually in the server or in how the theme builds the top of the page. I audit both and give you a fix order per template, backed by field data. See my Core Web Vitals audit and consulting work.