Skip to Main Content

Case Study: How to identify & fix Core Web Vitals issues

In this Case study of my website, I am going to show you how I identify and fix the Core Web Vitals reported on the Page Speed Insights - field data. You will learn what mistakes I made and you can avoid

Pass Core Web Vitals

I noticed my Core Web Vitals (CWV) issues way back in June. Whatever optimization I have learned in the past few years, I have applied everything & we can see the improvement in the images below.

Want to know How I pass the Core Web Vitals Assessment? Just follow along, and you will learn how I did it and how I made mistakes so you can take precautions and never do it. 

(August) CrUX/ Chrome UX Report Dashboard-

The CrUX Dashboard is built with a Data Studio feature called Community Connectors. This connector is a pre-established link between the raw CrUX data on BigQuery and the visualizations of Data Studio. It eliminates the need for users of the dashboard to write any queries or generate any charts. Everything is built for you; all you need is to provide an origin and a custom dashboard will be generated for you.

Here is my CrUX dashboard for my origin, if you want to see your CruX. You can head over to Page Speed Compare.

Page Speed Compare

First Contentful Paint (FCP) –

First Contentful paint Performance report from Chrome User expereince

Larget Contentful Paint (LCP)

Largest Contentful Paint Performance report from Chrome User expereince

Cumulative Layout Shift (CLS)

Cumulative Layout Shift Performance report from Chrome User expereinceAs you can see I have no issue with Google Search Console – Core Web Vitals and Page experience report but the page speed Insights – field data is telling me something.

Google Search Console Report

Core Web Vitals Report

Google Search Console Report suggest there is no Core Web Vitals issue

Page Experience Report

Page Experience Report

Reasons behind the Core Web Vitals Assessment failure in PSI 

Page Speed Insights -Field data reporting I have failed the assassement on 26 August
Page speed insights – field data recorded on 26th August

FCP

To improve my LCP performance, I inline 2 relatively small JS ( web vitals JS initiator and lite-youtube js) and stylesheets. This way my LCP image loads earlier in the waterfall chart but when I implement this method, in a few days, the FCP performance starts to degrade and gets worse and even the Page Speed Insights (PSI) lab test starts to report there is an unnecessary pre-connect to YouTube CDN from inline JS which add unnecessary milliseconds to my FCP performance.

CLS

  1. I was experimenting CSS clamp function on my heading tags and for some reason, it was causing Layout shifts. ( I might be wrong here) 
  2. New up-to-date styles on margin, section width, etc were missing in the critical CSS generation process.CLS debug_target reported by Web Vitals JS libraries

LCP

FYI: While I was using the Cloudinary plugin, I use the rel=”preconnect” to preconnect the Cloudinary domains, remove the loading=”lazy” attribute & add the fetchpriority=”high” priority hints to my featured image.

Dynamic Image Syntax set up for feature image
Dynamic featured image syntax

Even though I still suffer and never improve my LCP metrics because of the added connection time made by the Cloudinary. (See more details in comparisons).

I love using Cloudinary as my image & video CDN. As you can see my featured image/ hero image is the largest element painted on the viewport (LCP) according to the CWV report & webpagetest result👇 and my Image CDN is the biggest culprit in slowing down my LCP performance.

Featured image as LCP candidate reported by webpagetest
My featured image is the largest element painted accordingly to the Webpagetest result
LCP debug_target reported by Web Vitals JS libraries
You can see numbers 2nd and 4th in the Highest Metric values by Debug Target which refers to my Featured image

Self-host/ sub-domain vs 3rd parties comparisons

According to the webpagetest test run by CSS Wizardry, the time spent resolving a new origin added 509ms to the overall time spent downloading the LCP image and when I tested it on my own it added 524ms.

Now, if my response time/ Time To First Byte is bad which I have no control over, it can take more time to download the resources, and also it can affect my Largest Contentful Paint, First Contentful Paint.

CSS Wizardry

CSS Wizardry - Web page test

CSS wizardry - Requests details

As per the CSS wizardry webpagetest test, it also added around 509ms Total Connection Time.

With Cloudinary

With Cloudinary- Web page test

Cloudinary test results - Requests details

Delivering images from Cloudinary added 524ms in Total Connection Time accordingly toWebpagetest.

Sub-Domain/ CNAME integration

I am currently using Autoptimize with Shortpixel Adaptive image service integration to deliver avif images when browser supported.

Sub-domain integration with Cloudflare- Web page test

Shortpixel Adaptive Images -Request details

ShortPixel Adaptive AI sub-domain integration with Autoptimize plugin added 0ms just like self-hosted, you can see the webpagetest result in details views.

Self-hosted image 

Self-Hosted AKA Cloudflare- Web page test

Self hosed image added 0 ms where image CDN add more than 500ms

Self-hosted image added 0ms in Total Connection Time, you can see the webpagetest results in the details view.

You can see from the above comparisons, self-hosted images outperform image CDN even though it provides amazing features like-

  1. Serve Next-Gen images format without creating multiple image formats.
  2. Compressing images on the fly without optimizing manually. If you test some of my posts, you will see some of my feature images are not optimized before then I was heavily dependent on Cloudinary.

I am not criticizing the service at all. While I was using it, I get all the advantages that CDN provides and now I am doing everything manually like compressing.

Importance of RUM solution

Web Vitals JS

I have been using web-vitals JS libraries for over a year to monitor and collect Core Web Vitals metrics daily. When I set up the libraries, I send the debug identifiers to Google Analytics 4 to BigQuery and then Data Studio to analyze and visualize the data because of this I know exactly which selectors cause the layout shift & which is the largest element painted on the viewport.

This web vitals JS libraries version I am currently using doesn’t support other optional metrics like INP, FCP, and TTFB.

Data studio show the highest Metric values by debug target for LCP
Data studio shows the highest Metric values by debug target for LCP

See the images below 👇that is the Core Web Vitals Report I created.

Data studio shows the highest Matric values by debug target for CLS
Data studio shows the highest Metric values by debug target for CLS

With these insights from the CWV Report and a combination of lab data testing tools. Identifying, analyzing & fixing the issues is easier compared to those who do not set up any RUM solution.

# Ready to start identifying and monitoring your real user experience with the web-vitals JS libraries? Follow the web.dev article on how to set it up – Debug Performance issues with web-vitals js

# If you want an advanced version, you can follow Tony McCreath’s article on Web Site Advantage Core Web Vitals Tracking

Cloudflare’s Browser Insights

If you’re using Cloudflare’s Browser insights, you have already collected lots of data from real users. I set it up for more than a week and have similar results with the web vitals JS setup.

Images shows which URL suffers the Largest Contentful Paint (LCP) metrics
URL shows the Largest Contentful Paint (LCP) metrics
Image shows which HTML elements shows the bad or good Latgest Contentful paint
Image shows which HTML elements show the bad or good Latgest Contentful paint
URL has Layout Shift
URL needs an improvement
Elements responsible for causing Layout Shift
Elements responsible for triggering Layout instability

Fixes & Comparisons-

You will not see this field data in CrUX Dashboard because I fixed the issues in late August. You will be able to see it in the next CrUX update i.e. September and all the metrics are improving.

  1. To improve LCP performance: I deliver my images with the help of Autoptimize Shortpixel Adaptive AI integration & then with the rel=”preload” hints to preload my feature image. So, browsers put the request in higher priority.
If your featured image is a background image, consider preloading or inlining the CSS rendering the background image so browsers discover it earlier.

Comparisons of Largest Contentful Paint (LCP)

2.7 sec Largest Contentful paint which is not good Core Web Vitals
Before
After the Largest contentful paint fixes
after
  1. To fix the CLS issues: I included the up-to-date CSS in the critical CSS process. First, generate the used CSS with the Chrome extension I recommend in my removed unused CSS article, and then in the second step, I regenerate Critical CSS with the online Critical Path CSS Generator and inject the CSS into the head tag of the page.

Comparisons of Cumulative Layout Shift (CLS) 

0.11 score on Layout Shift which is bad, it means the content will be move around when the page load which is bad User Expereince
Before
After the fixes, there is no Cummulative layout detected
after
  1. To improve my FCP performance, I remove most of the redundant CSS like the !important CSS, and duplicate style, removing the unnecessary preconnect call by the Lite-YouTube embedded JS.
If in my article YouTube videos are above-the-fold, then preconnect calls are necessary.
        
        //Before 
initImagePlaceholder() {
    LiteYTEmbed.addPrefetch('preconnect', 'https://i.ytimg.com/');
    const posterUrlWebp = `https://i.ytimg.com/vi_webp/${this.videoId}/${this.posterQuality}.webp`;
    const posterUrlJpeg = `https://i.ytimg.com/vi/${this.videoId}/${this.posterQuality}.jpg`;
    this.domRefImg.fallback.loading = this.posterLoading;
    this.domRefImg.webp.srcset = posterUrlWebp;
    this.domRefImg.jpeg.srcset = posterUrlJpeg;
    this.domRefImg.fallback.src = posterUrlJpeg;
    this.domRefImg.fallback.setAttribute('aria-label', `${this.videoPlay}: ${this.videoTitle}`);
    this.domRefImg?.fallback?.setAttribute('alt', `${this.videoPlay}: ${this.videoTitle}`);
}

        
        
        // After
 initImagePlaceholder() {        
    const posterUrlWebp = `https://i.ytimg.com/vi_webp/${this.videoId}/${this.posterQuality}.webp`;
    const posterUrlJpeg = `https://i.ytimg.com/vi/${this.videoId}/${this.posterQuality}.jpg`;
    this.domRefImg.fallback.loading = this.posterLoading;
    this.domRefImg.webp.srcset = posterUrlWebp;
    this.domRefImg.jpeg.srcset = posterUrlJpeg;
    this.domRefImg.fallback.src = posterUrlJpeg;
    this.domRefImg.fallback.setAttribute('aria-label', `${this.videoPlay}: ${this.videoTitle}`);
    this.domRefImg?.fallback?.setAttribute('alt', `${this.videoPlay}: ${this.videoTitle}`);
}

        

Comparison of First Contentful Paint (FCP)

FCP recorded on 26th August
Before
1.7 sec First contentful paint performance recorded after the fix
after

Charts over the time

Daily Data collected from Page Speed Insights Field data report

First Contentful Paint (FCP)

After the fixes, my FCP is gradually performing better (lower) than the results I got on the 25th of August
After the fixes, my FCP is gradually performing better (lower) than the results I got on the 25th of August

Largest Contentful Paint (LCP)

After I hosted my feature image locally, my LCP significantly improve my performance
After I hosted my feature image locally, my LCP significantly improve my performance

Cumulative Layout Shift (CLS)

Chart representing Cumulative Layout shift
Blank represent there is no layout shift detected

Lesson I learned

  1. When fixing Core Web Vitals issues, people miss the Server Response time or Time for First Byte (TTFB) metrics because it is optional but if you see it properly TTFB plays a huge role, if it fluctuates a bit it can affect your Largest Contentful Paint (LCP), and First Contentful Paint (FCP) metrics even when you fully optimize your website. 
  2. Serving and delivering LCP image/ critical images on different origins or 3rd parties domains is a bad idea because this adds unnecessary time spent downloading the LCP image/ critical images. This also applies to other static resources too so better reduces unnecessary network calls. You can see the results in comparison.
  3. If you’re changing/updating the style just make sure you regenerate Critical CSS on that specific page or if you’re updating on a single post template, consider regenerating Critical CSS for all the pages. This makes sure the Critical CSS is included in the process. If you’re using CDN to deliver your assets, consider preconnect to those origins. This can helps FCP’s performance
  4. Unnecessary, do not inline your CSS and JS to avoid the warnings and for the page speed score (watch the HTTP 203 YouTube video below). I saw lots of tutorials recommending this terrible approach. It is valid even if I did so with a relatively small JS and I saw a huge impact on my INP performance. You will not be able to see the effects on any lab data testing tools but it can affect your real user’s performance like rendering performance, INP, FCP, etc.  
  5. Always try to reduce early 3rd parties domain calls.
If you’re using Elementor, do not set the CSS Print Method to Internal Embedding in Elementor > Settings > Advanced because It unnecessarily inlines your frontend CSS which can be over 30KB (Brotli compression) when Improved CSS loading experiments are turned off.

What’s Next

For my images, I haven’t decided whether to deliver them from Cloudinary. I will test the plugin properly in the localhost environment or staging site and then reconfigure the plugin.

A mistake I made was when I uninstalled the plugin I didn’t realize Cloudinary has the option to deliver images from an origin server. I should have tested the feature. If it doesn’t work next time, I will try alternative services and plugins.

UPDATE

I reconfigured and tested the Cloudinary plugin yesterday i.e 5th of October on my staging site and it didn’t work.

To serve the image from a sub-domain or CNAME, I have to purchase their Advanced Plan which I can’t afford.

Right now, I am using Shortpixel Adaptive AI (delivered image with Autoptimize plugins) with 4.99USD/month

Alternative 

If my website’s single post template has lots of components like motion effects (sticky header, parallax effects), sticky sidebar, sliders, gallery, etc then there is no point in using the alternative solution. As you can see from my article, my design is just basic and there is no point in adding all the features that will slow down my site performance.

My friend Massimo Villa raised a question when I requested him to review my article.

Since you use Elementor on your site, why do you use Lite-YouTube instead of the native facade (“overlay”) feature replacing YouTube player with local image placeholder?

Lite-YouTube JS VS Elementor Video

My website is built with Elementor (Affiliate) and it has its own Video Widget that supports YouTube, Vimeo, Dailymotion & Self-Hosted Video, and other amazing features like Lazy-loading, facade (“image overlay”) as Massimo Villa suggested.

Elementor Video widget settings
Elementor video widget's Overlay feature

Here is why I choose Lite-YouTube over the Elementor video widget-

  1. Performance – Lite-youtube JS is only 3.3KB whereas Elementor video can go over 70 to 80KB in page size because of dependencies.
  2. Lite-Youtube is just a vanilla web component where Elementor video is dependent on jQuery, frontend, frontend.modules, and whole lots of JS.

Image CDN

Here are some of the 3rd parties services that offer CNAME/ sub-domains like

  1. FlyingProxy (It is integrated with Cloudflare Enterprise)
  2. Webp express plugin preloading <picture> tag is trickier than <img> tag.
  3. Cloudflare Pro
  4. Shortpixel Adaptive AI
  5. Bunny Optimizer
  6. Imgix

Adoption

Currently, I am using the Autoptimize plugin with Shortpixel Adaptive AI integration to deliver images and serve in an avif image format when browsers support it.

Autoptimize images Settings

To preload the featured image, I use the Autoptimize metabox which you can enable in Misc Options – Enable configuration per post/ page.

Autoptimize metabox

Plugin Recommendations (Affiliate links):

All the plugins I recommend below have the feature for generating critical CSS, removing unused CSS, the ability to compress images, and serving in Next-Gen image format automatically such as-

  1. FlyingPress
  2. Wp Rocket
  3. SwiftPerformance
  4. Perfmatters
  5. Autoptimize + RapidLoad + Critical CSS API + Shortpixel Adaptive AI  (This is great if you already have caching enabled on your server and you don’t want any conflict)

Reference 

  1. Optimizing Largest Contentful Paint- CSSWizardry
  2. Web-Vitals JS libraries – Github
  3. Measure and debug performance with Google Analytics 4 and BigQuery- web.dev.
  4. 3rd parties RUM solution -Philip Walton

More information:

  1. Pagespeed Insights – Field Data report  vs CrUX
  2. RUM & CrUX difference
  3. Chrome UX report update

Conclusions 

It took me a few days to see the results in Pagespeed Insights – Field Data report after I fixed the issues because I had to aggregate data for all user experiences on my origin, not for the URL of the page.

If you still don’t have Field Data because you don’t get enough visits. I still recommend setting up the Core Web Vitals Report or any RUM solution I recommend in the tutorials I linked because RUM solutions will give you more insights into how your real users are experiencing when they visit your website than what you get from any lab data tools.

Suppose you are doing it for a client’s website or your website. I highly recommend setting up any RUM solution because it is easy to debug and identify the root causes. And you don’t have to give excuses later to your clients when they complain about your credibility.

Now tell me, how long will it take for you to fix the Core Web Vitals Assessment?

Let me know the answer in the comment below.

A huge Thank you to Massimo Villa for the corrections and for giving me more insights. I appreciate it. If you want to optimize your website, I highly recommend reading his article on how to get 100% in speed tests.

(September) CrUX/ Chrome UX Report Dashboard-

The only metric that will bite me in the future is the server response time or Time To First Byte (TTFB) metrics because I do not have any control and the content is delivered from Cloudflare’s CDN.

First Contentful Paint Metrics

First Contentful paint for September CrUX report
First Contentful paint for September CrUX report

Largest Contentful Paint Metrics

Largest Contentful paint for September CrUX report
Largest Contentful paint for September CrUX report

Cumulative Layout Shift Metrics

Cumulative Layout Shift for September CrUX report
Cumulative Layout Shift for September CrUX report
Thangjam Kishorchand

Thangjam Kishorchand

Hi there, this is my place where I write about my Elementor tips and tricks that I learned for the last 2 years. I am mostly active on Quora and Facebook. I love messing around with design trends like Variable Fonts, Dark Mode

Powered by Elementor pro

This site is powered by Elementor pro : Theme builder and it contains Affiliate links,which means that if you buy from my links, Foxscribbler will earn a small commission.This commission comes at no additional cost to you.

Scroll up further to Load all the comments...