Don't use CSS background-image for LCP content
Use <img> tags for hero and LCP images instead of CSS background images to enable early discovery by the browser's preload scanner.
CSS background images are discovered late in the rendering process, creating a waterfall delay:
- HTML downloads and parses.
- The CSS file is discovered and downloads.
- The CSS parses.
- The background image URL is discovered.
- The image finally starts downloading.
Using an <img> tag lets the browser's preload scanner discover images during the initial HTML parse, starting the image download immediately in parallel with CSS.
The impact is especially significant when the background image is in an external CSS file, because CSS download latency adds to the discovery delay.
- You can use
fetchpriority="high"to prioritize the LCP image. - You can use
<link rel="preload">if needed. - Responsive image support with
srcsetandsizesis better. - It's easier to add
widthandheightfor CLS prevention. - It works with Shopify's
image_tagfilter and automatic optimization.
Anchor to When background images are acceptableWhen background images are acceptable
- Decorative patterns or textures.
- Non-LCP content elements.
- Elements outside the initial viewport.
- Small UI elements where discovery latency doesn't matter.
- Data URIs or inline SVGs, because there's no network request.
Use an <img> tag with absolute positioning to achieve the same visual effect as a background image. Load the image conditionally based on section position using section.index.
Write the condition as a positive section.index > 2 test rather than section.index <= 2. section.index is nil in the online store editor, in static sections, and in the Section Rendering API, and comparisons against nil are falsey, which would send the hero to the lazy branch. For the full explanation, refer to Load above-the-fold and below-the-fold sections differently with section.index.
HTML structure:
CSS for positioning:
The image needs no z-index, because .hero__content already stacks above it. Don't add a negative one: position: relative alone doesn't make .hero a stacking context, so z-index: -1 escapes it and can paint behind a section or wrapper background, leaving the LCP element blank. Dawn and Horizon both position their hero media this way.
Anchor to ExamplesExamples
Anchor to Anti-pattern: inline background imageAnti-pattern: inline background image
Anchor to Anti-pattern: external CSS background imageAnti-pattern: external CSS background image
Anchor to Recommended: ,[object Object], tag with positioningRecommended: <img> tag with positioning
<img> tag with positioningBoth anti-patterns convert to the same markup. When the hero is a static section that always renders at the top of the page, the image is unconditionally eager and no section.index test is needed:
When a merchant can move the section anywhere on the page, use the section.index version in How instead. Both versions need the CSS from that section so that the image fills the section and the content stacks above it.
Anchor to TestingTesting
Use the Chrome DevTools Network panel to compare before and after:
- Open the DevTools Network tab and throttle to Slow 3G to see the waterfall clearly.
- Look at when the hero image request starts in the waterfall.
- Background image: starts after the CSS file completes.
<img>tag: starts immediately during the HTML parse.
Anchor to ReferencesReferences
- Mark the LCP image with
fetchpriority="high" - Never lazy-load the LCP image
- Use
preloadresource hints sparingly - Use responsive images
- Load above-the-fold and below-the-fold sections differently with
section.index image_tagfilterimage_urlfiltersection.index- The stacking context on MDN