How to Optimize Client-Side Web Performance
Measure real user experience, reproduce problems in the browser, and fix the actual LCP, INP, or CLS bottleneck. Includes a practical workflow, code examples, and troubleshooting.
Optimize client-side web performance by measuring real user experience first, reproducing the problem under controlled conditions, and fixing the bottleneck users actually encounter. Track the three current Core Web Vitals: Largest Contentful Paint (LCP) for loading, Interaction to Next Paint (INP) for responsiveness, and Cumulative Layout Shift (CLS) for visual stability. Aim for LCP of 2.5 seconds or less, INP of 200 milliseconds or less, and CLS of 0.1 or less at the 75th percentile, measured separately for mobile and desktop. Google’s Web Vitals guidance defines these thresholds. A passing lab score alone does not prove that real users improved.
1. Establish a baseline with field data
Field data records visits from real users, including their devices, networks, page states, and interactions. Lab data runs a page under controlled conditions and helps you inspect causes and compare changes. Use both: field data tells you where users struggle; lab data helps explain what to change.
Start with the 75th percentile for each metric and segment it by mobile and desktop. Break results down by representative page template, route, and user journey: an aggregate site score can hide a slow product page or an interaction that fails only on mobile. Record the date, browser conditions, route, build, and any interaction sequence so you can compare like with like.
| Metric | Experience | Good target at the 75th percentile | First diagnostic question |
|---|---|---|---|
| LCP | Loading | ≤ 2.5 seconds | What was the final LCP element, and when did its resource start loading? |
| INP | Responsiveness | ≤ 200 milliseconds | Which real interaction was slow, and what work occupied the main thread? |
| CLS | Visual stability | ≤ 0.1 | Which visible elements moved, and what caused the shift? |
These are population-facing thresholds, not a promise about every individual visit. Look at distributions and affected journeys as well as pass/fail labels. Google notes that the 75th percentile is intended to represent a level met or exceeded by most visits; check the threshold methodology for context.
2. Reproduce the affected experience in a lab
- Choose a representative URL and state: include relevant cookies, account state, viewport, and the user path that produced the issue.
- Open Chrome DevTools’ Performance panel and record a page load. For an INP problem, record the actual click, tap, or keyboard interaction too; a load-only run cannot measure INP directly.
- Use Lighthouse for repeatable load diagnostics and audits. Use WebPageTest when you need to specify device and network conditions. PageSpeed Insights can show available field data alongside a lab run.
- Repeat runs with the same route, device, network, cache state, and interaction sequence. Compare the trace and metric details, not just a single summary score.
- Change one measured cause at a time, then rerun. After release, check field data to confirm the improvement reached users and did not regress another metric.
Google’s measurement guide explains the tool roles and limitations. Lighthouse’s Total Blocking Time (TBT) is a useful lab signal for main-thread blocking, but it is not an interchangeable INP score. INP requires user interactions and field or interactive measurement. Load-only lab runs can also miss layout shifts caused later in a session.
Optional: collect Core Web Vitals in the browser
The web-vitals library can report metric values to your analytics endpoint. Install it in an existing JavaScript project with npm install web-vitals, then adapt the endpoint and payload to your telemetry system. This is a client-side measurement example, not a complete analytics backend:
import {onCLS, onINP, onLCP} from 'web-vitals';
function sendToAnalytics(metric) {
const body = JSON.stringify({
name: metric.name,
value: metric.value,
id: metric.id,
route: location.pathname,
deviceClass: matchMedia('(max-width: 767px)').matches ? 'mobile' : 'desktop'
});
// Replace this URL with your own same-origin collection endpoint.
navigator.sendBeacon('/analytics/web-vitals', body);
}
onLCP(sendToAnalytics);
onINP(sendToAnalytics);
onCLS(sendToAnalytics);
Use a privacy-appropriate collection endpoint and avoid sending unnecessary personal data. Make sure your aggregation keeps route and device segments, and uses the 75th percentile for the field comparison. The library reports values; your server or analytics platform must store and aggregate them.
3. Fix the measured LCP cause
LCP is the render time of the largest visible image, text block, or video candidate. The candidate can change while the page loads, so inspect the final reported element and its resource. LCP may include connection setup, redirects, and server response time; an image optimization will not fix a delay that occurs before the browser can request that image.
- Find the element and its timing. Inspect the LCP element and network waterfall in DevTools or a lab report. Separate the delay before the resource request from download and render time.
- Make critical content discoverable early. Put important image URLs in initial HTML when possible. If JavaScript injection or a CSS background hides the critical asset until later, consider whether the markup can expose it sooner.
- Do not lazy-load the visible LCP image. Lazy loading delays the request until the browser confirms the image is in view. Reserve lazy loading for below-the-fold content.
- Set priority only when measurement supports it. For a late-discovered critical image, selectively use preload or
fetchpriority="high". Too many preloads compete for early bandwidth and can delay other critical resources. - Check upstream delay. If the resource starts late because the document response is late, investigate redirects, connection setup, and server response before changing image bytes.
<!-- A visible hero image discoverable from the initial HTML -->
<img
src="/images/hero-960.webp"
srcset="/images/hero-640.webp 640w, /images/hero-960.webp 960w, /images/hero-1600.webp 1600w"
sizes="(max-width: 700px) 100vw, 960px"
width="1600"
height="900"
fetchpriority="high"
alt="Description of the page's main image"
>
If measurement shows the image is otherwise discovered late, a preload can help. Use the same responsive selection logic as the image itself, and verify the network waterfall after adding it:
<link
rel="preload"
as="image"
href="/images/hero-960.webp"
imagesrcset="/images/hero-640.webp 640w, /images/hero-960.webp 960w, /images/hero-1600.webp 1600w"
imagesizes="(max-width: 700px) 100vw, 960px"
>
Do not add both preload and high priority reflexively: first confirm the browser is discovering the asset too late or assigning it insufficient priority. The LCP optimization guide covers resource discovery and priority; its preload guidance warns that excessive hints can compete for bandwidth.
4. Fix the measured INP cause
INP captures responsiveness across qualifying interactions over the page lifecycle. A page-load-only test has no interaction to measure. Use field data or an interactive recording to identify the slow interaction, then inspect the event’s input delay, event-handler work, and time until the next paint.
- Reduce main-thread work. Remove unnecessary JavaScript, defer noncritical work, and split large tasks so input and rendering can proceed sooner.
- Inspect the interaction path. Profile the specific handler and the code it triggers. Expensive DOM updates, repeated layout reads and writes, large synchronous calculations, and unnecessary third-party work can all delay a response.
- Keep feedback prompt. Render a lightweight response to the user’s action before doing nonurgent work. Schedule work that can wait rather than making the interaction handler do everything synchronously.
- Measure real actions again. Test the same click, keyboard, or touch path after each change. Use TBT as a load-time clue to blocking, not as proof that field INP improved.
Capture interactions in DevTools’ Performance panel, then inspect long tasks and the main-thread call stack around the delayed response. For a page with many controls, prioritize the interactions users rely on instead of optimizing an unrelated load trace. See Google’s INP guidance and its measurement guide.
5. Fix the measured CLS cause
CLS reflects unexpected movement of visible content. Find the shifting elements and what caused them to move; image compression alone does not address shifts caused by fonts, inserted content, or later page behavior.
- Reserve image dimensions. Add intrinsic
widthandheight, or reserve the correct aspect ratio in CSS, so the browser can allocate space before the image arrives. - Serve responsive images. Use
srcsetandsizeswhere appropriate to select assets that fit the viewport while preserving the intended ratio. - Make space for late content. Ads, embeds, banners, and asynchronous widgets should not push existing content unexpectedly. Reserve their space or place them in a layout that can accommodate them.
- Check fonts and interaction states. Font swaps and user-triggered updates can change geometry. Reproduce the affected state and inspect actual shifts.
- Look beyond initial load. Compare field data and longer interactive sessions with load-only lab captures; the latter may not include later shifts.
<img
src="/images/article-800.webp"
srcset="/images/article-400.webp 400w, /images/article-800.webp 800w"
sizes="(max-width: 600px) 100vw, 800px"
width="800"
height="450"
alt="Description of the article image"
>
The dimensions let the browser reserve a 16:9 box before the image loads. Confirm that the declared ratio matches the delivered image. See Google’s CLS optimization guidance.
6. Validate improvements without trading one problem for another
- Repeat the same lab setup and user interaction before and after the change.
- Check the relevant metric and inspect its underlying trace or element details.
- Review LCP, INP, and CLS together after substantial changes; a priority hint, script change, or reserved region can affect another part of the experience.
- Release the change and allow field data to accumulate, then compare the same routes, device segments, and percentiles.
- Keep the optimization only when the relevant field experience improves without a meaningful regression elsewhere.
There is no universal speed trick that works independently of the page and its users. Optimize against a recorded baseline, and keep the conditions of each comparison consistent.
7. Troubleshooting common performance investigations
| Symptom | Likely cause | What to do |
|---|---|---|
| Lighthouse looks good but users report slow interactions | A load-only run did not exercise the slow interaction; TBT is only a lab proxy for INP. | Use field INP or record the actual interaction in DevTools. Profile the handler and main-thread work. |
| Field CLS is worse than the initial-load report | Shifts happen later, after interaction, or when asynchronous content arrives. | Inspect the full page lifecycle and reproduce the later state. Reserve space for content that loads after the initial render. |
| The hero image downloads late despite being small | It may be injected by JavaScript, hidden in CSS, lazy-loaded, or assigned a lower priority. | Inspect request start time and discovery path. Expose it in initial HTML, remove lazy loading if it is the LCP image, and test a selective priority hint. |
| Adding preload made another resource slower | Too many resources now compete for early bandwidth. | Remove unnecessary preloads and retain only measured critical assets. Recheck the waterfall on constrained conditions. |
| Compressed images did not improve LCP | The delay may be connection setup, redirects, server response, late discovery, or rendering. | Break down LCP timing and find where time is spent before optimizing another resource. |
| An image still causes layout movement | Dimensions may be missing, the declared ratio may not match, or a different element is shifting. | Inspect the shift source, correct intrinsic dimensions or CSS aspect ratio, and check fonts and injected content too. |
| Metric values vary greatly between runs | Network, device, cache state, page data, and user interaction differ. | Control lab conditions, repeat runs, compare distributions, and use field data to understand real variability. |
| A route has no usable field result | There may be insufficient available field data for that route or segment. | Use representative lab measurements and first-party field instrumentation; do not treat missing data as a pass. |
8. Performance, reliability, and cost considerations
Performance work has an ongoing measurement cost: field data needs instrumentation or an available field dataset, and lab runs need repeatable conditions. Start with the failing metric and representative routes so measurement effort follows user impact. Avoid collecting more user data than needed to segment and diagnose the experience.
For reliability, record the test setup and repeat runs. A single score can be noisy; a stable improvement across controlled runs is a better release signal, while field data confirms how the change behaves across actual users and devices. Keep monitoring all three metrics because a change that speeds loading can still add script work or cause layout movement.
For common context, Google’s web.dev summary reported that 40% of sites in the Chrome UX Report did not meet the recommended good LCP threshold, and that 73% of mobile pages had an image as their LCP element in the 2024 Web Almanac. These are figures reported in that summary, not fresh 2026 measurements or a prediction about an individual page. They suggest image discovery is often worth investigating, but your own LCP element and field data should determine the fix.
Or skip the browser setup
ScreenshotNeo is a website screenshot API and MCP server from Yorker Media. It returns a screenshot or PDF from one GET request, which is useful when a performance investigation needs repeatable visual captures across URLs or viewports. A screenshot helps inspect rendered output; it does not measure LCP, INP, or CLS, so keep using field and browser performance tools for those metrics.
Cookie banners are accepted and removed before capture, along with 60+ known consent platforms, newsletter popups, and chat widgets; each step can be turned off. Bot checks/CAPTCHAs, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status. Its MCP server gives AI agents tools for screenshots, page info, and PDF capture.
Example cURL request (replace the URL with the page you want to inspect):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://stripe.com -o shot.webp
Python:
import requests
r = requests.get(
"https://api.screenshotneo.com/v1/shot",
params={"access_key": "YOUR_API_KEY", "url": "https://stripe.com"},
timeout=90,
)
open("shot.webp", "wb").write(r.content)
Node.js:
const q = new URLSearchParams({ access_key: 'YOUR_API_KEY', url: 'https://stripe.com' });
const res = await fetch(`https://api.screenshotneo.com/v1/shot?${q}`);
if (!res.ok) throw new Error(`Screenshot request failed: ${res.status}`);
await import('node:fs/promises').then(fs => fs.writeFile('shot.webp', Buffer.from(await res.arrayBuffer())));
See the ScreenshotNeo API documentation for request options. The free plan includes 1,000 screenshots per month with no card; paid plans start at $5 for 3,000. Sign up for 1,000 free screenshots a month, with no card.
FAQ
Does optimizing Core Web Vitals guarantee a faster site for every visitor?
No. The thresholds summarize population-level field experience at the 75th percentile. Individual visits vary by device, connection, route, and interaction.
Can a screenshot tell me whether my page passes Core Web Vitals?
No. A screenshot shows rendered appearance at a point in time. Use field metrics and browser performance recordings to measure experience and diagnose timing.
Should I optimize the homepage first?
Start with field data and the journeys or templates with the clearest user impact. The homepage may not represent the route or interaction that is failing.
Is TBT the same as INP?
No. TBT is a lab diagnostic for blocking during page load. INP measures responsiveness to interactions and should be assessed with interaction or field data.


