Core Web Vitals for image-heavy portfolio sites
Portfolio sites pass Core Web Vitals when one image is prioritised and the rest wait. The thresholds, the lab-versus-field gap, and the order I fix things in.
By Zaid Rais, design engineer, Amsterdam. 1448 words.

Image-heavy portfolio sites pass Core Web Vitals when one image is prioritised and everything else waits. Google's thresholds are LCP under 2.5 seconds, INP under 200 milliseconds and CLS under 0.1, measured at the 75th percentile of real visits. Photography is not the problem. Loading all of it at once is.
Which Core Web Vital actually breaks a portfolio site?
Largest Contentful Paint, in nearly every case. On a studio homepage the largest element in the viewport is the hero photograph, so LCP is a direct measurement of how long that one file takes to be discovered, fetched, decoded and painted. Everything else on the page is a rounding error next to it.
The three metrics and their thresholds are published by Google on web.dev. Worth reading properly, because two details in the definition change how you should test:
The scores are taken from real visits, not from your laptop. Chrome reports them into the Chrome User Experience Report, aggregated over a rolling 28-day window.
The number that counts is the 75th percentile. Your fast visitors do not rescue you. A page can feel instant on a Mac on office fibre and still fail, because a quarter of real sessions are on a three-year-old Android phone on a train.
Cumulative Layout Shift is the second offender on gallery pages, and it is almost always self-inflicted. Interaction to Next Paint is rarely a problem on a portfolio, with two exceptions I will come to.
Why does Lighthouse show 100 when Search Console says the page is slow?
Because they measure different things. Lighthouse is a lab test: one run, one simulated device, a synthetic network. Search Console reports field data from actual Chrome users over 28 days. A perfect lab score and a failing field score is a normal, informative disagreement, not a bug.
Lighthouse's mobile preset applies a simulated slow 4G connection and a four-times CPU slowdown to a single cold load. That is a useful, repeatable diagnosis of why a page is slow. It cannot tell you whether your visitors are slow, because it does not know that half of them arrive from an Instagram in-app browser on a mid-range handset.
So I use both, for different jobs. Field data from the CrUX dataset answers whether there is a problem. Lighthouse answers what is causing it. When I score a site's speed before writing a proposal I run PageSpeed Insights on mobile, which shows the two side by side; speed is the smallest of the three weights in the rubric I score sites against, but it is the only one a machine can settle without argument.
How do I fix LCP when the hero is a photograph?
By making the browser find and fetch that single image before anything else, and by making the file small enough to arrive in about a second on a slow connection. Those are two separate problems and both have to be solved.
Never lazy-load the hero
Adding loading="lazy" to every image is the most common piece of well-meant damage I find. On the hero it delays discovery until layout has run, which can add a second or more to LCP on mobile. Lazy-load everything below the fold; never the LCP element.
The same applies to heroes injected by JavaScript. A slider or carousel that builds its first slide after hydration hides the most important file on the page behind a script download, a parse and an execution. If the first slide is static markup, LCP stops being a scripting problem.
Tell the browser it matters
fetchpriority="high" on the hero image, and a preload if the image is discovered late, are documented in Google's fetch priority guidance. They cost one attribute each. On a page whose entire first impression is one photograph, that attribute is worth more than any amount of build tooling.
Size the file to the container, not to the camera
A 4000 pixel wide export from a photographer's delivery folder is not a web image. Serve modern formats, with srcset so a phone downloads a phone-sized file. My working budget, a rule of thumb rather than a measurement: hero under 200 KB, no single WebP over 180 KB, and a first viewport under about 1 MB in total transfer. Where the export comes from and what to ask a photographer for is the subject of my note on photography for interior design sites.
Preload one font weight, and only one
Type that swaps late shifts the layout and delays text paint. font-display: swap plus a preload of the single weight used above the fold is usually enough. Four weights preloaded is four files competing with the hero for bandwidth at the exact moment the hero needs all of it.
What actually causes layout shift on a gallery page?
Three things, in order of frequency: images without declared dimensions, fonts swapping into a different metric, and banners injected after paint.
An image with no width and height attributes, or no CSS aspect-ratio, occupies zero height until it loads and then pushes everything below it down. In a masonry grid of thirty photographs that produces a page which visibly reflows for several seconds. The fix is boring and complete: every image element carries its intrinsic ratio, so the space is reserved before the pixels arrive.
Cookie banners and newsletter overlays that push content rather than overlay it are the other reliable cause. If it has to appear, give it a fixed position over the page, not a slot in the flow.
The threshold is 0.1, and it is easy to sit at zero. A gallery that shifts is a gallery nobody has looked at on a phone.
Does INP matter on a site that is mostly pictures?
Yes, at two moments: the lightbox and the filter. Interaction to Next Paint replaced First Input Delay as a Core Web Vital in March 2024, and unlike its predecessor it measures the full response, not just the delay before handling starts.
On portfolio sites the pattern that fails is a project filter that re-renders a large grid on every click, or a lightbox that decodes a full-resolution image on the main thread while animating. Both are fixable by decoding off the main thread and by keeping the number of DOM nodes in a gallery sensible. Scroll-driven animation libraries that attach unthrottled listeners are the other usual suspect.
What does this look like on a page I actually built?
The opening film on this site is 288 hand-sequenced frames drawn onto a canvas rather than a video element. That sounds like the worst possible idea for LCP, and it would be if all 288 arrived together. They do not. The first frame is a normal, prioritised image that paints as the LCP element; the remainder stream in behind it and the sequence only starts once enough are buffered. On my machine the draw loop holds around 117 frames per second, but the number that matters is that the first painted frame does not wait for the other 287.
Journal headers here go through a compression step to WebP with a hard ceiling under 180 KB, and every page is plain HTML underneath with no theme layer, which is most of why the interaction numbers stay boring. More on how I work is on the about page, and the cost side of building this way is in what an architecture website costs in 2026.
What do I check, in order?
This is the sequence I run on any image-heavy site, and it resolves most failures before anything clever is needed.
- Identify the LCP element in Lighthouse. It is usually not the one you assumed.
- Confirm that element is in the initial HTML, not injected by script.
- Remove
loading="lazy"from it; addfetchpriority="high". - Check its transfer size on mobile. Anything over 200 KB gets re-exported.
- Confirm
srcsetis serving a phone-sized file to a phone. - Give every image a declared aspect ratio.
- Preload one font weight; let the rest load normally.
- Re-test in the field after 28 days, not in the lab after five minutes.
That last point is the one studios find hardest. Field data moves on a rolling month, so a fix deployed today shows up in Search Console gradually over the following four weeks. The lab confirms you did the right thing; only the field confirms it mattered. The award-winning sites I look at when writing about what makes an architecture website win awards are not fast because they use fewer photographs. They are fast because exactly one photograph is allowed to be urgent.
Questions people ask
- What is a good LCP for a photography-led homepage?
- Under 2.5 seconds at the 75th percentile of real mobile visits, which is Google's published threshold. On a hero photograph that usually means a file under about 200 KB, served in a modern format, present in the initial HTML and marked high priority.
- Why does my Lighthouse score differ from Search Console?
- Lighthouse is a single lab run on a simulated slow device. Search Console reports field data from real Chrome users, aggregated over a rolling 28-day window at the 75th percentile. Lighthouse tells you why a page is slow; the field data tells you whether it is.
- Should I lazy-load images on a portfolio site?
- Yes, everything below the fold. Never the hero. Lazy-loading the Largest Contentful Paint element delays its discovery and is the most common cause of a failing LCP on studio websites.
- How many photographs can one page carry?
- As many as you like, provided only the first viewport loads eagerly. As a rule of thumb I keep the first view under about 1 MB of transfer and let the rest arrive on scroll. Page weight below the fold barely affects Core Web Vitals.
- How long before a fix shows up in Search Console?
- Around four weeks. Field data is aggregated over a rolling 28-day window, so improvements appear gradually rather than immediately. Verify the fix in Lighthouse on the day, then confirm it in the field a month later.
Zaid Rais designs and builds websites, search and AI-answer visibility, and AI assistants for people with an eye for design, from Amsterdam. Read more on the atelier, or book a consultation.