Magento (Adobe Commerce) websites could be slow.
There is a direct link between user experience and conversion. Faster checkouts lead to more sales.
It’s important to make web pages load quickly and Core Web Vitals (CWVs) can help you with that.
CWVs measure loading speed, interactivity and visual stability.
I’ll show you how to optimize Magento Core Web Vitals and reach green scores on Google PageSpeed Insights and Lighthouse.
But firstly, a little bit of theory:
NB: if you want to skip theory, head over to the first optimization section.
From here on we will use Magento Luma Theme for demonstration purposes.
- Profile code to lower time to first byte (TTFB)
- Perform a 3rd-party Extension Audit
- Inspect Theme Files
- Full Page Cache
- Use Varnish as Caching Application
- Remove Unused CSS
- Inline Critical CSS
- Enable HTML minification
- Do not lazy load an LCP image!
- Preload LCP element
- Serve images in next-generation formats: WebP and AVIF
- Serve different image sizes for different platforms
- Optimize web fonts
- Set image dimensions
- Use srcset For Responsive Images
- Optimize Web Fonts Loading and Rendering
- Make Pages Eligible for Back/Forward Cache (bfcache)
- Reduce Size and Number of 3rd-party JavaScript Code Files
- Remove Unused JavaScript
- Use the content-visibility CSS property
- Avoid excessive DOM size
What are Core Web Vitals?
CWVs aim to quantify user experience (UX).
There are three main aspects of UX: loading, interactivity and visual stability.
Let’s briefly focus on each of them:
1. Largest Contentful Paint (LCP) - Loading
LCP reports time from clicking a link to seeing the largest image or text block above-the-fold.
Here is an example of what an LCP block might look like:
The sooner that text is visible the better.
Anything below 2.5 seconds is considered good LCP.
Between 2.5 seconds and 4 seconds - it needs improvement.
Above 4 seconds - poor LCP.
2. Interaction to Next Paint (INP) - Interactivity
It tells how fast a page responds to mouse clicks or screen taps.
Sluggish drop down menus and accordion lists irritate users and lead to lost sales.
Good INP is something below 200 ms.
500ms or bigger INPs provide poor user experience and are considered bad.
3. Cumulative Layout Shift (CLS) - Visual Stability
This web vital measures unexpected page content movements.
Dynamically loaded HTML blocks or ads could shift content up and down.
The image below could illustrate why visual stability is important:
Place Order button was loaded by AJAX with a significant delay.
You were about to click on Save for Later but instead placed an order due to a layout shift.
CLS is a relative measurement.
Anything below 0.1 is considered good.
Anything above 0.25 is bad.
How to Optimize Magento Largest Contentful Paint (LCP)
You might want your LCP resource (text block or an image) to load and render as soon as possible for good LCP.
Adobe Commerce website has platform-specific optimization tweaks that will help you with that.
You might need to apply different techniques to different templates.
Use Google PageSpeed Insights to measure and diagnose LCP.
1. Profile code to lower time to first byte (TTFB)
LCP depends on how fast a browser gets HTML from the server.
Time to first byte (TTFB) is how long it takes from clicking a link to receiving data.
You need to make TTFB as low as possible. Code profilers will help you with that.
What is a code profiler?
It’s a special tool you connect to a web server that logs all code function calls with corresponding run times.
It then displays a callgraph:
You can identify the longest calls and optimize them. Sometimes you can just turn off an extension to improve server response time.
There are various online code profilers: New Relic, Tideways, Blackfire. Most of them offer trial periods so you would have a chance to explore them before buying.
Check out my detailed guide on how to optimize Magento 2 TTFB for more information.
2. Perform a 3rd-party Extension Audit
Magento 2 could deliver a poor server response time due to a faulty custom module.
In fact, 90% of the sites I optimized had that same problem - a slow running plugin.
Find out all the plugins you have installed by running the following command:
php bin/magento module:status
Ignore core modules that start with Magento_ and focus on everything else.
What you can do now is simple: turn all custom extensions off and see if it makes any difference.
The following command will turn a module off:
php bin/magento module:disable Module_Name
If the site becomes faster, try turning plugins back on one by one.
To turn a plugin on run the following:
php bin/magento module:enable Module_Name
Identify the one after which Magento is slow again. That module needs to be investigated, substituted or completely removed from the system.
3. Inspect Theme Files
Your Magento 2 custom theme could be a reason behind the long TTFB.
I’d suggest trying to turn it off and look to see if it makes any difference in speed.
Here is how to switch to the default Luma theme.
Go to backend menu Content > Design > Configuration:
Click on Edit on Global row:
And select Magento Luma next to Applied Theme:
Once the Luma is activated, browse the website and see if you notice a difference in TTFB.
You can use Google Chrome Developer tools > Network tab:
Green line is server response time.
If the website does become faster you might need to inspect theme files closely.
Code profilers can help you with investigation.
4. Full Page Cache
Magento 2 implements full page cache to deliver fast TTFB.
Double check this option is turned on at System > Cache Management:
If it’s off you can enable it by running the following command line:
php bin/magento cache:enable full_page
5. Use Varnish as Caching Application
Varnish is a special caching server with very fast response times.
Magento 2 and Adobe Commerce can store full page cache either on disk or in Varnish.
I recommend using Varnish as a Caching Application for better performance.
Go to System > Configuration > Advanced > System > Full Page Cache > Caching Application and set it to Varnish:
Export the correct VCL file for your version and pass it to your hosting support team.
They should also supply you with backend host and port information.
After setup is complete verify it works.
Varnish TTFB for a cached page should be around 100ms.
6. Remove Unused CSS
Stylesheets are render-blocking.
Magento tends to have huge CSS files which will increase LCP. It pays to try and remove unused styles to speed up loading.
We will use Google Chrome Coverage tab to identify CSS directives to be deleted.
To open up the coverage tab go to Google Developer Tools and click on three dots:
Then click on Run Command and type Show Coverage:
Here is a typical diagram of a Luma theme category page:
As you can see, the biggest CSS file has 89% redundant data (for this specific page).
Chrome lets you see what directives are unused (red bars):
You will need to copy styles next to gray bars into a special file category-file.css. Do it for every CSS file listed in the coverage tab.
Next, patch Magento codebase to exclude all styles to only include category-file.css. That way you speed up CSS loading and lower LCP.
7. Inline Critical CSS
You can split category-file.css into critical and non-critical parts.
Critical CSS is styles used to render above-the-fold content. Above-the-fold content is a visible page part before you scroll down.
By inlining critical CSS you improve LCP even further. Less CSS to load - better largest contentful paint time.
How to isolate critical styles?
There are CSS generators like penthouse and Critical npm package.
But I’d recommend the same google chrome coverage tab, it’s more accurate.
This time you don’t need to scroll down the page after initial loading.
Interact with above-the-fold content record what CSS is being used. Open dropdown menus, click links etc.
Then copy used styles into a special category-critical.css file.
Patch code to inline that file in the head page section.
Exclude critical styles from category-file.css and move it to the bottom of the page.
Magento 2.4+ can inline CSS automatically.
You can turn it on at System > Configuration > Advanced > Developer > CSS Settings > Use CSS Critical Path (you need to be in the developer mode):
php bin/magento deploy:mode:set developer
The critical file should be located at app/design/frontend/YOUR_THEME_VENDOR/YOUR_THEME_NAME/web/css/critical.css.
You can take a look at the default Luma critical file at vendor/magento/theme-frontend-luma/web/css/critical.css.
The system will use one critical file for all pages. You will have to patch code to use different files for different templates.
You can also turn on Critical CSS on command line:
php bin/magento config:set dev/css/use_css_critical_path 1
8. Enable HTML minification
HTML minification speeds up page loading and improves LCP.
To enable it - switch to the developer mode:
php bin/magento deploy:mode:set developer
And go to System > Configuration > Advanced > Developer > Template Settings > Minify HTML:
Set it to Yes, click save and return to the production mode (minification doesn’t work with developer mode):
php bin/magento deploy:mode:set production
9. Do not lazy load an LCP image!
If your LCP resource is an image - make sure it’s not lazy loaded.
Why is it bad?
A delayed largest contentful paint image means increased LCP. And that means a poor core web vitals score.
Excluding an image from lazy loading depends on what extension you are using.
Many of them offer a specific CSS class:
10. Preload LCP element
Sometimes an LCP image is:
- Loaded with a CSS file
- Dynamically added by JavasScript
- Lazy loaded and couldn’t be excluded
In all these cases it’s important to preload the image to optimize LCP.
You can use a special link tag the head HTML section:
<link rel=”preload” fetchpriority=”high” href=”/image.jpg” type=”image/jpeg”/>
You can use fetch priority separately:
<img src=”/image.jpg” fetchpriority=”high”/>
11. Serve images in next-generation formats: WebP and AVIF
You can improve LCP by reducing the size of the largest contentful paint image.
Converting JPEGs and PNGs into WebPs and AVIFs could cut weight more than in half while preserving quality.
There is a manual bash command to do a conversion:
cwebp -q 80 image.png -o image.webp
ls -hl image.png
1.2Mb
ls -hl image.webp
83Kb
More than 10x size reduction!
There are Magento extensions available that do webp migration automatically. Just browse Adobe commerce marketplace to find ones with positive reviews.
Some older browsers don’t support WebP. Use a
<picture> <source type=”image/webp” srcset=”/image.webp”/> <img src=”/image.png”/> </picture>
12. Serve different image sizes for different platforms
Using a desktop 1900x1000 px image for mobile platforms is never optimal.
You can improve LCP by serving smaller resources for smaller viewport sizes.
This is called responsive images and it could be done with a srcset directive.
For example, a picture tag with the following:
<picture> <source type=”image/webp” srcset=”/image1.webp 1910w, /image2.webp 1024w, /image3.webp 767w”/> <source type=”image/jpeg” srcset=”/image1.jpeg 1910w, /image2.jpeg 1024w, /image3.jpeg 767w”/> <image src=”/image.jpg”/> </picture>
…tells a browser image name with corresponding image width in pixels. Browser then picks the right picture based on the user screen width.
Only the image picked by the browser gets loaded. Desktop images never load on mobile platforms leading to improved performance.
Srcset attribute could be used with an img tag as well:
<img src=”/image.jpeg” srcset=”image-small.jpeg 400w, image-large.jpeg 1024w”/>
13. Optimize web fonts
In case your LCP resource is a text block, using a custom web font for it could slow down LCP.
Before the font files are loaded and processed, text is invisible leading to high largest contentful paint and poor web vital scores.
Here are several things you could do:
- Don’t use custom fonts, rely on system fonts for better performance
- If you have to use them, preload files for faster processing:
<head> …… <link rel=”preload” href=”/font.woff2” type=”font/woff2” as=”font” crossorigin> </head>
- Use the latest and the lightest WOFF2 font format. Avoid using EOT, TIFF and WOFF as they are bigger in size and slower to render.
How to Optimize Magento Cumulative Layout Shift (CLS)
CLS is all about visual stability. Page that loads with no content jumping up and down will have a good cumulative layout shift score.
Various techniques are used to optimize CLS. There are a few Magento specific ones which I’ll cover below.
14. Set image dimensions
Magento 2 can set product/category image height/width automatically.
But you might need to set placeholders if you use lazy loading.
You can set width/height for a lazy loading image holding container to reserve space.
<span class="product-image-container product-image-container-15" style="width: 240px;height 300px;"> <span class="product-image-wrapper" style="padding-bottom: 125%;"> <img class="product-image-photo" src="domain/media/catalog/product/cache/e1afd103f52aeb0169b76d2109c6500e/u/g/ug06-lb-0.jpg" loading="lazy" width="240" height="300" alt="Affirm Water Bottle "></span> </span>
There might be images with no specific dimensions set. Those are typically found on the homepage, blog and cms pages.
Run a page through Google PageSpeed Insights. It will highlight images you need to adjust.
You will need to either edit .phtml templates or edit CMS content in the Magento admin panel.
Using built-in Pagebuilder could make edits easier.
15. Use srcset For Responsive Images
Sometimes you can’t set fixed resource width and height. The cases include responsive images that resize with viewport.
Setting width=100% would impact CLS score and you need a different approach.
I’m suggesting to use a srcset attribute. It can hold several image variants with widths. Modern browsers can then choose what resource to display based on the viewport.
<picture> <source type="image/webp" srcset="/media/banner.webp 1911w, /media/banner-1024.webp 1024w, /media/banner-640.webp 640w, /media/banner-399.webp 399w"><source type="image/jpg" srcset="/media/banner.jpg 1911w, /media/banner-1024.jpg 1024w, /media/banner-640.jpg 640w, /media/banner-399.jpg 399w"> <img src="/media/banner.jpg" non-lazy="" data-nowebp="true"> </picture>
Here you list an image (/media/banner-1024.webp) and its width(1024w). Browser then picks the best picture based on the window size.
16. Optimize Web Fonts Loading and Rendering
Custom web fonts could shift layout and impact CLS score.
There are several ways to avoid that:
- Use in head section to load fonts as early as possible
- Add font-display: optional CSS style to skip font swapping if custom font isn’t available during initial rendering
- Set a fallback font: font-family: ‘Custom Font’, sans-serif. If no fallback font is set, the browser will be the default font which in most cases is worse than sens-serif.
Another approach is to inline font definitions and files. Just add them to the critical CSS file.
Refer to Section 8 of this article for more information.
17. Make Pages Eligible for Back/Forward Cache (bfcache)
Bfcache is a browser feature that lets you navigate back and forward fast. It works by storing whole pages in memory.
Back/Forward cache can significantly improve cumulative layout shift. First page visits can have some layout shift. Next visits will have CLS at zero because of bfcache.
Not all pages are eligible though. Here is how to see if a page is bfcache-friendly:
Open up Chrome Developer Tools and go to Applications > Background services > Back/forward cache:
Click on the Test button and see the results.
Here are several issues that could prevent pages from storing in bfcache:
- Cache-control: no-store - having that header will instruct a browser to skip cache.
- unload event listener - never use unload if you want to take advantage of bfcache.
How to Optimize Magento Interaction to Next Paint (INP)
INP tells us how fast a page responds to user interactions. A site visitor can select dropdowns, fill in forms, click on links and perform other actions.
The longest interaction usually represents INP. For an optimal page, next paint time should be less than 200 milliseconds.
INP replaced First Input Delay (FID) as a stable core web vital.
Below you will find several tips on how to cut INP.
18. Reduce Size and Number of 3rd-party JavaScript Code Files
Pages with a high number of external JS files tend to have higher INP. Browsers can be busy running that Google Ads script and respond slowly to user input.
There is a performance measure - Total Blocking Time (TBT). It shows how long the main browser thread was blocked. When the main thread is blocked, browsers don’t respond to user interactions.
TBT is high when there are many 3rd-party Javascript files. Those could be google adsense, GA4, recaptcha, google tag manager etc.
Do an audit of what external services you place on the website. Remove those you don’t need.
19. Remove Unused JavaScript
Magento 2 could load JS files it barely uses. For example, take a look at Chrome Coverage tab for a sample Adobe Commerce site:
Notice 97.1% of swatch-renderer.js is redundant:
That’s around 50kB of wasted code that needed to be parsed and compiled. Browsers could’ve done other tasks instead of chewing through code we don’t even use.
I’d suggest analyzing each file and seeing if you could remove or cut it. You can rearrange page layout so that it doesn’t need some libraries.
For example, if you don’t use swatches, you can turn off Magento_Swatches module and remove swatch-renderer.js all together.
This task could be hard to do but rewards are well worth trying.
20. Use the content-visibility CSS property
content-visibility is a new CSS property. It lets you delay rendering and painting certain page blocks.
For example, you can set content-visibility: auto on below-the-fold content. Browsers wouldn’t layout and render it. This could free the main-thread to do more important things.
This feature can improve page responsiveness and lead to better INP. Just remember that browser support could be limited.
21. Avoid excessive DOM size
Page’s HTML is organized into DOM (Document Object Model). It represents all elements as a tree.
Having DOM tree branches too big could negatively affect performance. Browsers would spend more time on rendering, styling and layout.
You need to keep track of three numbers:
- Total DOM elements
- Maximum DOM depth
- Maximum child elements
Make sure these numbers aren’t big. For example, having more than 3000 DOM elements is a concern. You would need to audit your Magento theme templates and lower it.
A rule of thumb here - do not over-complicate your layout. Take the Magento Luma theme as your reference and keep it simple.
Takeaway
Core Web Vitals play an important role in today’s eCommerce world. You need to make your Magento 2 store pass the assessment:
It’s not that hard to achieve it. You just have to build your Adobe Commerce shop with these in mind:
- Keep it simple. Slightly modified Luma Theme is better than a bloated over-engineered custom theme.
- Do not use too many 3rd-party scripts, services and Magento extensions.
- Rely on full page cache, Varnish and cache warmers.
If you need help with Core Web Vitals - do not hesitate to contact an expert.
If you find this post interesting do not hesitate to sign up for our newsletter and join the 1472 people who receive Magento news, tips and tricks regularly.
Thank You!