How to Reduce LCP in E-commerce Stores
LCP (Largest Contentful Paint) is the most visible Core Web Vitals metric. It measures how long it takes for the largest element in the viewport to fully render. For e-commerce, this is almost always the hero image on homepage or the main product image on PDP. An LCP above 2.5 seconds is considered poor by Google and directly impacts ranking. In online stores, slow LCP means the user waits for the main content to appear. If the product image takes long to load, the first impression is slowness. The user does not know (nor cares) if the problem is the image, the server or JavaScript. They only know the page is slow. And they leave. Reducing LCP requires attacking multiple fronts simultaneously: server response time, critical rendering path, resource loading and correct prioritization. This article details each cause and presents practical solutions tested in real VTEX, Shopify and headless stores.
What causes high LCP in e-commerce
LCP is composed of four subphases. Each one can be the bottleneck. TTFB (Time to First Byte): the time between the browser request and the first response byte. Includes DNS, TCP, TLS and server processing. If TTFB is 1.5s, you only have 1 second for everything else to stay within the 2.5s limit. Resource load delay: time between TTFB and start of LCP resource download. Caused by dependency chains. The browser needs to download HTML, parse it, find CSS, download CSS, parse it, and only then discover it needs the image. Resource load duration: time to download the LCP resource itself. Large images on slow connections. A 500KB hero image on 3G can take 3-4 seconds just to download. Render delay: time between resource download and screen rendering. Caused by blocking JavaScript, CSS that hides the element or fonts that delay text rendering. In e-commerce stores, the most common culprits are: unoptimized hero images (format, size, lack of preload), web fonts that block rendering, non-critical CSS in head, slow server response time (heavy backend, lack of cache), JavaScript that blocks rendering and carousels with incorrect lazy loading on the first image.
Hero image and main product image optimization
The hero image is the LCP element in 70% of e-commerce homepages. The main product image is the LCP in 90% of PDPs. Optimizing them is the highest impact action. Preload: add link rel preload for the LCP image in the HTML head. This tells the browser to start downloading the image immediately, without waiting for CSS to be parsed. For responsive images, use imagesrcset in preload. Format and compression: serve WebP or AVIF. A hero image that would be 400KB in JPEG becomes 250KB in WebP and 180KB in AVIF. Size reduction translates directly to shorter download time. Correct size: do not serve a 1920x1080 image to a 375px phone. Use srcset with breakpoints covering real device sizes of your audience. Check Google Analytics for most common resolutions. Fetchpriority high: add fetchpriority high to the LCP image tag. This instructs the browser to prioritize this image download over other resources. Works in Chrome and Edge. Avoid lazy loading on LCP: the LCP image should never have loading lazy. This delays download start until the image enters the viewport, adding hundreds of milliseconds to LCP. Only below-fold images should have lazy loading. Inlining small images: for LCP images smaller than 10KB, consider inline as base64 in HTML. Eliminates a network roundtrip. Useful for logos and critical icons, not large hero images.
Font loading and impact on text LCP
When LCP is a text element (heading, paragraph), web fonts can significantly delay rendering. The browser only renders text with the custom font after it is downloaded. If the font takes 800ms, text stays invisible (FOIT) or changes appearance (FOUT) for 800ms. font-display swap: the CSS property font-display swap instructs the browser to show text with a fallback font immediately and swap to the custom font when loaded. Text appears fast (good for LCP) but may cause a visual flash (CLS impact). It is the most accepted trade-off. Font preload: add link rel preload for the most critical font files. Limit to 1-2 fonts maximum. Each preloaded font competes for bandwidth with other resources. Font subsetting: if you use a font only for headings in English, generate a subset including only Latin characters. A complete font can be 200KB. A Latin subset stays below 30KB. Tools like glyphhanger and subfont automate this. Self-hosting vs CDN: hosting fonts on the same domain eliminates the need to connect to external domains (fonts.googleapis.com). This removes DNS lookup, TCP and TLS handshake. Self-hosting with aggressive cache is always faster than Google Fonts. Variable fonts: a single variable font replaces multiple files (regular, bold, italic). Reduces requests and allows more efficient caching. If you use 3+ weights of the same family, variable font is the right choice.
Critical CSS and render-blocking elimination
CSS is render-blocking by default. The browser renders nothing until all CSS in head is downloaded and parsed. If total CSS is 200KB but only 15KB is needed to render above-the-fold content, the other 185KB are blocking rendering unnecessarily. Critical CSS inline: extract the CSS needed for above-the-fold content and place inline in the HTML head. The rest loads asynchronously. Tools like Critical, Critters and PurgeCSS automate extraction. Async loading of remaining CSS: use the link rel preload with onload technique to load non-critical CSS without blocking rendering. The pattern is: link rel preload href styles.css as style onload this.rel stylesheet. This starts download immediately but only applies CSS when loaded, without blocking. Remove unused CSS: stores using CSS frameworks like Bootstrap or Tailwind frequently load the entire framework when only 10-20% is used. PurgeCSS, UnCSS and Tailwind native purge remove unused classes. In Tailwind 4, tree-shaking is automatic. Avoid @import in CSS: each @import creates a sequential request. The browser downloads the first CSS, parses it, finds the @import, and only then starts downloading the second. Use parallel link tags or concatenate files. Minification: always minify CSS in production. Removes spaces, comments and shorthand properties. Typical 15-25% reduction. Tools like cssnano and lightningcss do this automatically at build time.
Server response time and TTFB
TTFB (Time to First Byte) is the foundation of LCP. If the server takes 2 seconds to respond, it is mathematically impossible to have LCP below 2.5s. Ideal TTFB is below 200ms for most users. Page cache: the most effective way to reduce TTFB. Product, category and homepage pages rarely change on each request. Cache at CDN with webhook invalidation when data changes. Stale-while-revalidate to never serve a cold page. SSR with streaming: instead of waiting for the entire page to be generated on the server, send HTML progressively. React 18 with Suspense and streaming SSR allows sending the shell immediately and filling content as it becomes ready. The browser starts rendering sooner. Edge rendering: move page generation to the edge (Cloudflare Workers, Vercel Edge, Deno Deploy). Eliminates network latency between user and server. A page generated at the Sao Paulo edge arrives in 20-50ms for Brazilian users vs 200-400ms from a US server. Database optimization: slow queries are a common cause of high TTFB in stores with large catalogs. Proper indexes, query result cache, and read replicas reduce backend response time. Reduce redirects: each redirect adds a complete roundtrip (100-300ms). Audit unnecessary redirects. The canonical should be served directly. HTTP to HTTPS and www to non-www redirects should happen at the edge, not at the origin server.
Preconnect, preload and resource prioritization
Resource hints are instructions in HTML telling the browser which resources will be needed soon, allowing it to start fetching them early. preconnect: establishes connection (DNS + TCP + TLS) with third-party domains before the browser discovers it needs them. Use for image CDN, APIs and external fonts. Example: link rel preconnect href cdn.shopify.com. Typical savings: 100-300ms per domain. dns-prefetch: lighter version of preconnect that only does DNS lookup. Use for domains that might be used but not certainly. Zero cost if not used. preload: forces immediate download of a specific resource. Use for LCP image, main font and critical CSS. Caution: too many preloads dilute the benefit. Limit to 3-5 truly critical resources. Priority Hints (fetchpriority): attribute that allows defining high, low or auto priority for resources. fetchpriority high on LCP image. fetchpriority low on below-fold images. Modern browsers use this information to reorder the download queue. Practical strategy for e-commerce: preconnect for image CDN and pricing API. preload for hero image and main font. fetchpriority high on product image. defer on all non-critical scripts. async on analytics script. This combination typically reduces LCP by 300-800ms.
Platform-specific tips: VTEX, Shopify and headless
Each platform has its particularities for LCP optimization. VTEX Store Framework: the biggest villain is the JavaScript bundle. Remove unused apps (each app adds JS). Use treeshaking in the theme. Configure VTEX Image Server to serve WebP automatically. Avoid heavy custom CSS in the theme. Native VTEX prefetch already preloads routes. For VTEX stores that cannot get LCP below 3s on Store Framework, consider migrating the frontend to FastStore or deco.cx while keeping the VTEX backend. VTEX FastStore: already optimized by default. Next.js with Image component does lazy loading and responsive images automatically. Ensure hero image has priority true. Use next/font for automatic font optimization. Configure cache headers on Vercel or deploy provider. Shopify: Shopify CDN does automatic WebP conversion. Use section rendering API to lazy load below-fold sections. Preload hero image in theme.liquid. Be careful with Shopify App Store apps that inject heavy JavaScript. Each app can add 50-200KB. Audit regularly. Shopify Hydrogen: full control over loading. Use Remix defer for streaming. Configure granular cache headers per route. Hydrogen Image component optimizes automatically. Headless (Next.js, deco.cx): maximum control. Use next/image or equivalent component. Configure ISR for cache. Implement streaming SSR. Do aggressive code splitting. LCP in well-implemented headless consistently stays below 1.5s.