
How to improve Core Web Vitals on a WordPress site?
To improve Core Web Vitals on a WordPress site, you need to make your main content appear faster (Largest Contentful Paint), make the page respond quickly when people click or type (Interaction to Next Paint), and stop elements from jumping around as the page loads (Cumulative Layout Shift). In practice, that means fast hosting with page caching, optimized and correctly sized images, a preloaded hero image, less and lighter JavaScript, efficient font loading, and reserved space for anything that loads late.
Core Web Vitals are Google's way of measuring real-world user experience, and they're part of the page experience signals Google considers in search. More importantly, they reflect how your site actually feels to visitors. This guide explains what each metric means, how to measure it correctly, and the specific WordPress fixes that improve each one. For general speed tips, you can also check my separate guide on speeding up a WordPress website.
What Are Core Web Vitals?
Core Web Vitals are three metrics that measure loading, interactivity, and visual stability. Google evaluates them at the 75th percentile of real visits, meaning at least 75% of page loads should meet the "good" threshold.
| Metric | What It Measures | Good | Needs Improvement | Poor |
|---|---|---|---|---|
| LCP (Largest Contentful Paint) | How quickly the largest visible element renders | 2.5 s or less | 2.5 s to 4.0 s | Over 4.0 s |
| INP (Interaction to Next Paint) | How quickly the page responds to interactions | 200 ms or less | 200 ms to 500 ms | Over 500 ms |
| CLS (Cumulative Layout Shift) | How much content unexpectedly moves | 0.1 or less | 0.1 to 0.25 | Over 0.25 |
INP replaced First Input Delay (FID) as a Core Web Vital in March 2024. It's a stricter metric because it looks at the responsiveness of all interactions during a visit, not just the first one.
How to Measure Core Web Vitals
Before fixing anything, find out where you stand. There are two kinds of data:
- Field data: Measurements from real Chrome users, collected in the Chrome User Experience Report (CrUX). This is what Google uses.
- Lab data: Simulated tests run by tools like Lighthouse. It's great for debugging, but it doesn't always match real-world results.
Here are the tools to use:
-
PageSpeed Insights: Enter a URL to see field data at the top (if your site has enough traffic) and a Lighthouse lab report below with specific recommendations.
-
Google Search Console: Open Experience > Core Web Vitals to see which groups of URLs on your site pass or fail, separated by mobile and desktop.
-
Chrome DevTools: The Performance panel shows live LCP, INP, and CLS values as you interact with a page, and highlights which element is the LCP element and which elements shift.
-
Site Kit by Google: The official plugin can show PageSpeed Insights data inside your WordPress dashboard.
Focus on mobile results first. Mobile devices are slower and mobile scores are usually worse, so improvements there help the most.
Improving Largest Contentful Paint (LCP)
LCP measures when the largest image or text block in the viewport finishes rendering. On most WordPress sites, the LCP element is a hero image, a featured image, or a large heading.
LCP is influenced by four stages: the server response time (TTFB), how long it takes to start loading the LCP resource, how long that resource takes to download, and how long before it's rendered. Here's how to improve each.
Reduce Server Response Time
If your server takes a second to respond, you've already used a big chunk of your 2.5-second budget.
- Use good hosting: Quality managed WordPress hosting or a well-configured VPS makes a big difference compared to overcrowded shared hosting.
- Enable page caching: Serving cached HTML skips PHP and the database for most visitors. Use your host's server cache or a plugin like WP Rocket, LiteSpeed Cache, W3 Total Cache, or WP Super Cache.
- Use a recent PHP version: PHP 8.2 or newer is noticeably faster than older versions. Check Tools > Site Health > Info > Server and upgrade from your hosting control panel.
- Add a persistent object cache: Redis or Memcached helps dynamic and logged-in pages that can't be page-cached.
- Use a CDN: A CDN reduces latency for visitors far from your server and can cache full HTML pages at the edge.
Optimize the LCP Image
- Use modern formats: WordPress supports WebP and AVIF uploads. Converting images to these formats often shrinks them significantly compared with JPEG or PNG.
- Size images correctly: Don't upload a 5000-pixel-wide photo to display at 1200 pixels. WordPress generates multiple sizes and uses
srcset, so the browser can pick the right one. - Compress images: Plugins like ShortPixel, Imagify, EWWW Image Optimizer, and Smush compress and convert images automatically.
Don't Lazy-Load the LCP Image
Lazy loading is great for images below the fold, but lazy-loading your hero image delays LCP. WordPress core already skips lazy loading for the first large content image and adds fetchpriority="high" to the image it thinks is the LCP candidate. However, sliders, page builders, and some plugins override this.
Check your hero image's HTML in DevTools. It should not have loading="lazy", and ideally it should have fetchpriority="high". If you add images manually in a template, you can set these attributes yourself:
echo wp_get_attachment_image(
get_post_thumbnail_id(),
'large',
false,
array(
'loading' => 'eager',
'fetchpriority' => 'high',
)
);
If your caching plugin has a lazy-load feature, look for a setting to exclude the first one or two images.
Preload the LCP Image When It's Discovered Late
If your hero image is a CSS background or is injected by JavaScript, the browser can't find it until late in the loading process. A preload hint fixes that. Add this to a child theme's functions.php or a custom plugin, adjusting the conditions to match your site:
function sajjad_preload_hero_image() {
if ( ! is_front_page() ) {
return;
}
$hero_url = get_theme_file_uri( 'assets/images/hero.webp' );
printf(
'<link rel="preload" as="image" href="%s" fetchpriority="high">' . "\n",
esc_url( $hero_url )
);
}
add_action( 'wp_head', 'sajjad_preload_hero_image', 1 );
For hero images that are regular <img> tags with fetchpriority="high", a preload usually isn't necessary.
Remove Render-Blocking CSS and JavaScript
CSS and synchronous JavaScript in the <head> block rendering until they download. To reduce the impact:
- Remove unused plugins: Every plugin that loads its own CSS and JavaScript on every page adds weight.
- Defer non-critical JavaScript: Most caching plugins offer a "defer JavaScript" or "delay JavaScript" option.
- Generate critical CSS: WP Rocket, LiteSpeed Cache, and similar tools can inline the CSS needed for the first screen and load the rest later.
- Use a lightweight theme: Block themes and lean classic themes typically load far less CSS and JavaScript than heavy multipurpose themes.
Improving Interaction to Next Paint (INP)
INP measures the delay between a user interaction, such as a click, tap, or key press, and the next visual update. Poor INP is almost always caused by too much JavaScript running on the main thread.
Audit Your JavaScript
Open Chrome DevTools, go to the Coverage tab, and reload the page to see how much of each script is actually used. Then check which plugins load scripts on pages where they aren't needed. Common heavy hitters include:
- Page builders and their animation libraries
- Sliders and carousels
- Chat widgets and popups
- Social media embeds and share buttons
- Multiple analytics, advertising, and tracking scripts
Load Scripts Only Where They're Needed
Plugins like Perfmatters and Asset CleanUp let you disable specific scripts and styles on specific pages. You can also do it with code. For example, if a contact form plugin loads its assets on every page, you can dequeue them everywhere except your contact page. The exact handles depend on the plugin, so check the page source to find them:
function sajjad_dequeue_form_assets() {
if ( is_page( 'contact' ) ) {
return;
}
wp_dequeue_script( 'contact-form-7' );
wp_dequeue_style( 'contact-form-7' );
}
add_action( 'wp_enqueue_scripts', 'sajjad_dequeue_form_assets', 100 );
Use the Defer and Async Loading Strategies
Since WordPress 6.3, you can tell WordPress to load a script with defer or async directly when you enqueue it:
function sajjad_enqueue_scripts() {
wp_enqueue_script(
'sajjad-main',
get_theme_file_uri( 'assets/js/main.js' ),
array(),
'1.0.0',
array(
'strategy' => 'defer',
'in_footer' => true,
)
);
}
add_action( 'wp_enqueue_scripts', 'sajjad_enqueue_scripts' );
Deferred scripts download in parallel and run after the HTML is parsed, which frees up the main thread during loading.
Delay Third-Party Scripts
Many caching and performance plugins have a "delay JavaScript execution" feature that waits until the first user interaction before loading scripts like chat widgets, analytics, and ads. This can dramatically improve INP and LCP, but test carefully, since some scripts need to run immediately to work correctly.
Break Up Long Tasks in Custom Code
If you write custom JavaScript, avoid running long loops or heavy work directly in event handlers. Update the UI first, then yield to the browser before doing expensive work:
function yieldToMain() {
if (globalThis.scheduler?.yield) {
return scheduler.yield();
}
return new Promise((resolve) => setTimeout(resolve, 0));
}
document.querySelector('#filter-button')?.addEventListener('click', async (event) => {
event.currentTarget.classList.add('is-loading');
await yieldToMain();
runExpensiveFiltering();
event.currentTarget.classList.remove('is-loading');
});
Here, runExpensiveFiltering() stands in for your own function. The button gives instant visual feedback, and the heavy work happens after the browser has had a chance to paint.
Reduce DOM Size
Very large pages with thousands of HTML elements make every interaction slower, because the browser has more to recalculate. Heavy page builder layouts with deeply nested containers are a common cause. Simplifying layouts, paginating long lists, and using native blocks instead of nested builder sections can all help.
Improving Cumulative Layout Shift (CLS)
CLS measures how much visible content moves unexpectedly while the page is open. A classic example is starting to read an article, and then an ad loads at the top and pushes everything down.
Always Set Image and Video Dimensions
When images have width and height attributes, the browser can reserve the right amount of space before they load. WordPress adds these automatically for images inserted through the block editor and for wp_get_attachment_image(). Problems usually come from custom HTML, old content, or plugins. Make sure your CSS keeps images responsive without breaking their aspect ratio:
img {
max-width: 100%;
height: auto;
}
For embeds like videos and iframes, reserve space with aspect-ratio:
.video-embed iframe {
width: 100%;
height: auto;
aspect-ratio: 16 / 9;
}
Reserve Space for Ads, Banners, and Embeds
If an ad slot or cookie banner is injected after the page loads, give its container a fixed minimum height, or position banners so they overlay content rather than pushing it:
.ad-slot-header {
min-height: 250px;
}
Cookie consent banners should usually be fixed to the bottom of the screen so they don't shift the page content.
Fix Font-Related Layout Shifts
When a web font loads and replaces a fallback font, text can reflow if the two fonts have different sizes. To minimize this:
- Host fonts locally: Block themes can load fonts from Appearance > Editor > Styles > Typography using the Font Library, which stores them on your own server. Local fonts avoid an extra connection to a third-party domain.
- Use font-display: swap: This shows text immediately in a fallback font.
- Preload your main font: This helps it arrive before first render.
- Match fallback metrics: The
size-adjustdescriptor helps the fallback font take up the same space as your web font.
Here's an example of a well-configured font:
@font-face {
font-family: 'Inter';
src: url('/wp-content/themes/my-child-theme/assets/fonts/inter-var.woff2') format('woff2');
font-weight: 100 900;
font-style: normal;
font-display: swap;
}
If your theme uses theme.json, you can register fonts there instead, and WordPress will generate the @font-face rules for you:
{
"$schema": "https://schemas.wp.org/trunk/theme.json",
"version": 3,
"settings": {
"typography": {
"fontFamilies": [
{
"name": "Inter",
"slug": "inter",
"fontFamily": "Inter, sans-serif",
"fontFace": [
{
"fontFamily": "Inter",
"fontWeight": "100 900",
"fontStyle": "normal",
"fontDisplay": "swap",
"src": ["file:./assets/fonts/inter-var.woff2"]
}
]
}
]
}
}
}
Avoid Inserting Content Above Existing Content
Notification bars, "related posts" widgets loaded via JavaScript, and late-loading sliders can all push content down. Where possible, render these elements server-side so they're part of the initial HTML, or place them below the fold.
Watch Out for Animations
Animations that change top, left, width, or height can count as layout shifts. Use CSS transform and opacity for animations instead, since they don't trigger layout changes.
WordPress-Specific Quick Wins
Beyond the metric-specific fixes, a few WordPress-wide habits go a long way:
-
Keep WordPress, themes, and plugins updated: Recent WordPress releases have included many performance improvements, such as better image loading priorities and faster block rendering.
-
Try the Performance Lab plugin: Built by the WordPress Performance Team, it lets you test upcoming performance features, such as speculative loading improvements, modern image formats, and better image handling, before they land in core.
-
Take advantage of speculative loading: Since WordPress 6.8, core uses the Speculation Rules API to prefetch pages when visitors are likely to navigate to them, making subsequent page loads feel near-instant in supported browsers.
-
Limit plugins that add front-end assets: A plugin that only works in the admin has little front-end cost. A plugin that adds scripts and styles to every page does.
-
Replace heavy embeds with facades: Instead of loading a full YouTube player on page load, show a thumbnail and load the player only on click. Plugins like WP YouTube Lyte, and features in WP Rocket and Perfmatters, can do this.
-
Test after every major change: Performance can regress quietly when you add a plugin or change a theme.
How Long Until Improvements Show Up?
Lab tools like Lighthouse reflect your changes immediately. Field data takes longer. PageSpeed Insights and Search Console use a rolling 28-day window of real user data, so it can take up to four weeks for your improvements to be fully reflected. In Search Console, you can use Validate fix on a Core Web Vitals issue to start a monitoring period.
FAQ: Core Web Vitals on WordPress
A good score is LCP of 2.5 seconds or less, INP of 200 milliseconds or less, and CLS of 0.1 or less, measured at the 75th percentile of real page visits.
Yes, they're part of Google's page experience signals, but content relevance is still far more important. Good Core Web Vitals help most when competing pages are otherwise similar, and they improve user experience regardless.
The PageSpeed score comes from a simulated Lighthouse test, while Core Web Vitals in the field data section come from real users. Google uses the field data, so focus on that when it's available.
There's no single best plugin. A caching and optimization plugin like WP Rocket or LiteSpeed Cache, an image optimizer like ShortPixel or Imagify, and an asset manager like Perfmatters together cover most needs. Good hosting and a lightweight theme matter just as much.
Mobile devices have slower processors and often slower networks, so heavy JavaScript and large images hurt more. Optimizing images, reducing JavaScript, and improving server response times usually fixes mobile scores.
Interaction to Next Paint (INP) replaced First Input Delay as a Core Web Vital in March 2024. INP measures the responsiveness of all interactions during a visit rather than only the first one.
Yes, but it takes more effort. Page builders often add extra CSS, JavaScript, and nested HTML. Use their built-in performance settings, avoid unnecessary widgets and animations, and combine them with good caching.
Conclusion
Improving Core Web Vitals on WordPress comes down to three goals: show the main content quickly, respond to interactions instantly, and keep the layout stable. For LCP, focus on fast hosting, page caching, and a properly optimized, prioritized hero image. For INP, reduce and defer JavaScript, especially from plugins and third-party services. For CLS, set dimensions on images and embeds, reserve space for late-loading elements, and load fonts carefully.
Measure with PageSpeed Insights and Search Console, fix the biggest issue first, and retest after each change. Be patient with field data, since it takes a few weeks to update. With a lean theme, a thoughtful set of plugins, and good caching, passing Core Web Vitals on WordPress is very achievable, and your visitors will notice the difference long before Google does.


