WooCommerce Core Web Vitals fail for reasons a brochure site does not have. Cart sessions make pages harder to cache, product galleries and sliders compete for the LCP slot, filters and variation pickers run JavaScript on every click, and stores pile up marketing, review and payment scripts. My fix order on most stores: keep cache hits high for anonymous shoppers, make the main product or category image load first, cut the JavaScript work on product and category templates, and reserve space for everything that loads late. The targets are the usual ones at the 75th percentile of field data: LCP at 2.5 seconds or less, INP at 200 milliseconds or less, CLS at 0.1 or less.
This article stays on the three metrics. For general store speed, including the server and the database, see WooCommerce speed optimization. For how Core Web Vitals work on WordPress as a whole, read the complete guide to Core Web Vitals for WordPress.
Why is WooCommerce harder than a regular WordPress site?
A blog serves the same cached HTML to almost everyone. A store cannot, and that changes everything about LCP.
- Cart, checkout and my account pages are excluded from page cache, as they should be.
- Once a shopper adds something to the cart, WooCommerce sets cookies such as
woocommerce_items_in_cart. Depending on how your cache is configured, that visitor may skip the page cache on every page for the rest of the session. - Add-to-cart links, sorting and filter parameters create URL variants with query strings, and many cache setups treat each one as a miss.
- Each template has its own LCP element and its own scripts. Home, category, product and checkout fail for different reasons.
- Stores accumulate third-party scripts: analytics, ad pixels, chat, reviews, payment messaging. Each one adds main-thread work.
So I never look at a store’s Core Web Vitals as one number. I look at them per template, and then per device.
How do you find which store templates fail?
Start with the Search Console Core Web Vitals report. Its URL groups on a store usually map to product pages, category pages and content pages. Individual product URLs rarely have enough traffic for CrUX to report them alone, so you will often see origin-level or group-level data instead.
That is the main reason I add real user monitoring on stores. With the web-vitals library you can send the template name along with each metric, for example by checking is_product() or is_product_category() in PHP and printing it into the page. Then you can say “product pages fail INP on mobile” with your own data instead of guessing. The Core Web Vitals tools guide covers how each tool fits in.
How do you fix LCP on product and category pages?
On a product page, the LCP element is almost always the main gallery image. On a category page it is a banner or one of the first product thumbnails.
- Make sure the main product image is not lazy loaded. Since WordPress 6.3, core adds
fetchpriority="high"to the image it thinks is the most important one, but themes and gallery plugins change the markup. Check the rendered HTML instead of assuming. - Do not make the first image wait for JavaScript. WooCommerce’s gallery zoom, lightbox and slider are optional theme features (
wc-product-gallery-zoom,wc-product-gallery-lightbox,wc-product-gallery-slider). Whatever your theme enables, the first image must render without them. - Serve the right file size. Product images are often uploaded at print resolution. Correct
srcsetandsizeskeep phones from downloading a desktop image. - Do not lazy load the first row of products on category pages.
- Fix server response on uncached pages. Category pages with many filters and meta queries are slow to generate. A persistent object cache such as Redis object cache and a clean database help on every request that misses the page cache.
The full LCP breakdown, with the four parts of its timeline, is in LCP in WordPress: causes and fixes.
Why do WooCommerce stores fail INP?
INP measures how long the page takes to respond to clicks, taps and key presses. Stores have many of those, and most of them trigger JavaScript.
- Filter plugins that re-render the product grid on every checkbox.
- Variation forms, where choosing a size or color updates price, stock and image through JavaScript. Variation swatch plugins add more work on top.
- Add-to-cart buttons that update a mini cart, open a drawer and fire tracking events in the same task.
- Large DOMs on category pages with many products, each card carrying badges, ratings and hidden quick-view markup.
- Third-party scripts running long tasks right when the shopper taps something.
The fix is usually to do less on each interaction: yield to the main thread before tracking calls, load review and chat widgets later, reduce how many products render per page, and remove plugins that duplicate features. I go into the techniques in INP in WordPress: fixing slow interactions.
What about cart fragments?
The classic cart fragments script sends a wc-ajax=get_refreshed_fragments request to refresh the mini cart. That request is not page cached, so on busy stores it adds server load on every page view where it runs. Recent WooCommerce versions limit where the script loads, but themes and plugins still enqueue it. Open the network panel on a product page and check whether it fires. If your theme does not show a mini cart there, you probably do not need it.
Where does CLS come from on a store?
- Product images without width and height, or with a gallery that changes its height after the slider script starts.
- Price changes when a variation is selected, especially when a price range is replaced by a longer single price.
- Star ratings, “buy in installments” messages and stock badges that a script inserts after the page renders.
- Free shipping bars, coupon notices and consent banners pushed in above the content.
- Web fonts swapping late in product titles and prices.
Reserve the space. Set image dimensions, give widget containers a fixed or minimum height, and put notices in a slot that exists from the first paint. Details in CLS in WordPress: stop layout shifts.
What to check on each template
| Template | Usual LCP element | Usual INP trigger | Usual CLS source |
|---|---|---|---|
| Home | Hero banner or first slide | Menus, sliders, quick view | Promo bars, consent banners |
| Category | Banner or first product image | Filters, sorting, add to cart | Badges, ratings, late notices |
| Product | Main gallery image | Variation selection, gallery, add to cart | Price updates, payment messaging, reviews |
| Cart and checkout | Heading or form block | Quantity changes, shipping and payment fields | Notices, payment gateway iframes |
What about checkout?
Cart and checkout are usually not ranking pages, but slow interactions there cost sales. Payment gateways load their own scripts and iframes, which you control only partly. WooCommerce now ships Cart and Checkout blocks built with React alongside the classic shortcode checkout. They use a different front-end architecture, so if you migrate from one to the other, measure INP on checkout before and after instead of assuming either one is faster.
The fix order I use on stores
- Confirm the page cache works for anonymous visitors on home, category and product pages, and find what is causing bypasses.
- Fix the LCP image on product and category templates.
- Remove or delay third-party scripts that do not earn their place.
- Reduce JavaScript work on filters, variations and add to cart.
- Reserve space for every element that appears late.
- Watch the field data per template until the 28-day window reflects the change.
If your store keeps failing and you want someone to trace it template by template, that is the work I do as a Core Web Vitals expert.