Adding a customer gallery, testimonial wall, or Instagram feed to a static landing page is one of the fastest ways to build social proof. It is also one of the fastest ways to undo a fast page. The fix is not to avoid images — it is to lazy load website images correctly, so the pictures that matter paint first and everything else waits until someone scrolls toward it. This guide covers the standards-based mechanics: the loading attribute, intrinsic dimensions, responsive sources, priority hints, and how to confirm the result in DevTools.
Why added images can hurt Largest Contentful Paint
Largest Contentful Paint (LCP) is the Core Web Vital that measures when the largest image or text block in the viewport finishes rendering. The web.dev guide to optimizing LCP states that a good LCP value is 2.5 seconds or less, and that sites should aim to hit that for at least 75% of page visits. It also splits LCP into four sub-parts — time to first byte, resource load delay, resource load duration, and element render delay — and warns that optimizing one part can simply shift the saved time into another.
That matters for a landing page because you need to know which image is largest above the fold. That element is likely your LCP candidate. A new gallery can affect it in three ways: it can push the hero image further down the document so the browser discovers it later, it can compete with the hero for bandwidth, or it can become the LCP element itself if a large tile lands in the first screen. Images far below the fold behave differently — they cost nothing until the visitor approaches them.
Use `loading="lazy"` below the fold, not on the hero
The native loading attribute is the simplest tool available. According to MDN's <img> reference, eager loads the image immediately regardless of viewport position and is the default, while lazy defers loading until the image reaches a browser-calculated distance from the viewport. MDN notes that lazy loading avoids the network and storage bandwidth required to handle the image until it is reasonably certain to be needed, which improves performance in most typical cases.
So the rule is simple:
- Hero, logo, and first-screen product shots: leave them eager, or omit the attribute entirely.
- Gallery rows, feed thumbnails, and footer badges: add
loading="lazy".
<img src="hero.webp" alt="Product dashboard" width="1200" height="630">
<img src="gallery-01.webp" alt="Customer storefront" width="400" height="400" loading="lazy">
One caveat worth knowing: MDN notes that loading is only deferred when JavaScript is enabled, which is an anti-tracking measure. Treat the attribute as a performance enhancement, not a functional dependency — the page must still work if a lazy image never loads.
Reserve space with `width` and `height`
Lazy loading and layout stability are linked. MDN explicitly recommends explicit width and height on all images and calls them especially important for lazy-loaded ones: unloaded images have a width and height of 0, and lazy images that never intersect a visible viewport area never load. The result is a disruptive reflow in the middle of someone reading. Because the browser can compute the aspect ratio from those two attributes before the file arrives, it can reserve the correct box.
Your CSS can still control the displayed size:
.feed-thumb {
width: 100%;
height: auto;
display: block;
}
If your gallery markup comes from a template or an embed script you do not control, inspect the rendered DOM for missing dimensions. Placeholder boxes that collapse to zero height are the classic cause of a jumpy scroll as lazy images stream in.
Serve the right file with `srcset` and `sizes`
A lazy-loaded 2000px photo in a 300px card still costs bandwidth and decode time when it finally loads. MDN explains that sizes describes the intended display width so the browser can pick a matching candidate from srcset when sources use width descriptors. Without sizes, the default is 100vw, which tells the browser the image spans the full viewport — usually wrong for a grid tile.
<img
src="photo-800.webp"
srcset="photo-400.webp 400w, photo-800.webp 800w, photo-1200.webp 1200w"
sizes="(max-width: 600px) 50vw, 300px"
width="300" height="300"
alt="Customer photo"
loading="lazy">
MDN also documents sizes="auto", which lets the browser use the element's post-layout size. It is only valid with loading="lazy", so it suits gallery items well — include fallback sizes after auto for browsers that do not support it.
Prioritize the real LCP image and defer the rest
Two attributes help you steer the browser. fetchpriority="high" hints that an image should be fetched at high priority relative to other images, and it can be paired with a preload hint so the request starts during HTML parsing instead of waiting for the tag to be discovered.
<link rel="preload" as="image" href="hero.webp" fetchpriority="high">
Everything below the fold gets the opposite treatment: loading="lazy" and no priority hint. The web.dev breakdown treats resource load delay as its own sub-part, and that is exactly what you minimize when the LCP image starts loading as early as possible.
Applying this to an embeddable social feed
A social feed widget is a special case because its images are usually inserted by an embed script after the page has already painted. You often do not control the markup, so design the page around it:
- Place the widget container where it makes editorial sense and give it a fixed aspect ratio box so it cannot resize your hero as tiles arrive.
- Do not preload widget thumbnails. Preloading a dozen gallery images competes with your hero for bandwidth.
- Verify the rendered thumbnails in DevTools: check for
loading="lazy", pluswidthandheight, rather than assuming the template includes them. - Check that the first row is not your LCP element. If a large tile lands above the fold and is set to lazy, you have deferred your own LCP image.
- Keep real image URLs responsive. Smaller grid cells need smaller files, and
srcsetwith a sensiblesizeshandles that.
If you are still assembling the embed, the WidgetJar guide to embedding an Instagram feed on your website covers installation, and the broader piece on embedding a widget without touching your backend explains how a script-based embed sits alongside a static page. For swapping above-the-fold imagery later without editing HTML, see updating static HTML images dynamically.
Measure before and after
Do not guess — record a baseline, then change one thing at a time:
- Open DevTools, run a Performance recording, and identify the LCP element in the timeline.
- Run a Lighthouse audit in an incognito window so extensions cannot interfere, and note the LCP value.
- Throttle the Network panel to a realistic mobile profile and confirm below-the-fold images are not requested on initial load.
- Scroll to the gallery and check that thumbnails request only then, at appropriate sizes.
- Watch for layout shift: if content jumps when lazy images arrive, your
width/heightare missing or wrong. - Compare before and after LCP and keep the change only if the number improves.
The web.dev guidance is also clear that lab tools like Lighthouse are informative but that real user data is the truer signal. Use lab tests to confirm the mechanics, then trust field data when you have it.
Quick checklist
- Hero and first-screen images: eager, with
fetchpriority="high"and a preload link. - Below-the-fold images:
loading="lazy". - Every
<img>:width,height, and usefulalttext. - Grid images:
srcsetwith a realisticsizesvalue. - Feed container: a stable aspect ratio so it cannot shift the hero.
- Before-and-after LCP recorded in DevTools and Lighthouse.
Next step
If social proof imagery belongs on your landing page, the Instagram feed widget lets you configure a feed in the WidgetJar panel and install the embed script on your registered domain. Once it is live, run the checklist above against the rendered page — that is where you will see whether those gallery images are adding credibility or quietly dragging your LCP upward.