Core Web Vitals are three numbers Google uses to describe how a page feels to a real visitor: how fast the main content appears, how quickly the page reacts when someone clicks or types, and how much the layout jumps around while it loads. If you are building your first web app, you do not need to become a performance engineer. You need to know what each metric measures, what counts as a passing score, and which handful of habits keep you on the right side of the line. That is what this guide covers.
What Core Web Vitals are
The current set is three metrics, each covering one part of the experience:
- Largest Contentful Paint (LCP) measures loading.
- Interaction to Next Paint (INP) measures interactivity.
- Cumulative Layout Shift (CLS) measures visual stability.
web.dev describes the stable metrics as "the current set of Core Web Vitals that Chrome considers essential for great user experiences", and says stable metrics change no more than once a year. That matters if you read older tutorials: INP became a stable Core Web Vital in 2024, replacing First Input Delay (FID). Anything that still tells you to optimise FID is out of date.
The thresholds
Each metric has a "good" target, and web.dev also defines where "poor" starts.
| Metric | Measures | Good | Poor |
|---|---|---|---|
| LCP | Loading | 2.5 seconds or less | More than 4.0 seconds |
| INP | Responsiveness | 200 milliseconds or less | More than 500 milliseconds |
| CLS | Visual stability | 0.1 or less | More than 0.25 |
Anything between good and poor is "needs improvement".
The part people skip is how the score is judged. web.dev recommends measuring "the 75th percentile of page loads, segmented across mobile and desktop devices." In plain terms: three out of four real visits need to hit the target, not your fastest load on your own laptop, and mobile is judged separately from desktop. A page passes when all three metrics meet their targets at that 75th percentile.
Why they matter beyond a score
Google Search's documentation is direct about it: "We highly recommend site owners achieve good Core Web Vitals for success with Search". It adds that this, along with other page experience aspects, "aligns with what our core ranking systems seek to reward."
That is a recommendation, not a promise that fixing LCP will move you to the top of the results. The better reason to care is that each metric describes a moment where a visitor gets annoyed: a blank screen, a button that does nothing for half a second, a link that moves as you tap it.
Field data versus lab data
Field data comes from real users on real devices and connections. The Chrome User Experience Report (CrUX), PageSpeed Insights and the Core Web Vitals report in Search Console all show field data, and all support LCP, INP and CLS. This is what the thresholds are judged against.
Lab data comes from loading the page in a controlled, simulated environment. It is reproducible and useful for debugging, but web.dev notes a blind spot: "Tools like Lighthouse that load pages in a simulated environment without a user cannot measure INP." Lighthouse reports LCP and CLS, and uses Total Blocking Time as a lab stand-in for responsiveness. Chrome DevTools can measure all three while you interact with the page yourself.
A brand-new app has no field data yet, because it has no traffic. Use lab tools while you build, then check the field reports once real visitors arrive. If the two disagree, trust the field.
LCP: getting the main content on screen
LCP reports when the largest image, text block or video in the viewport finishes rendering. The candidates are images, images inside an SVG, videos, elements with a CSS background image, and block-level elements containing text. On most app landing pages the LCP element is either the hero image or the main headline.
web.dev's optimisation guide breaks LCP into four sub-parts and suggests rough proportions for each:
| Sub-part | What it is | Rough share |
|---|---|---|
| Time to First Byte | Waiting for the first byte of HTML | about 40% |
| Resource load delay | Gap before the LCP resource starts loading | under 10% |
| Resource load duration | Downloading the LCP resource | about 40% |
| Element render delay | Gap between loaded and painted | under 10% |
These are guidelines, not millisecond targets, but they show where to look. The practical fixes:
- Make the LCP resource discoverable in the HTML. If the hero image is only referenced from JavaScript or CSS, the browser finds it late. The guide says "your LCP resource should be discoverable from the HTML source."
- Never lazy-load the LCP image. The guide is blunt: lazy-loading it "will always lead to unnecessary resource load delay." Lazy loading is for images below the fold.
- Give the LCP image a high fetch priority. Mark the one image you expect to be the LCP element as high priority. Doing this on more than one or two images makes the hint less useful.
- Render on the server. Server-side rendering or prerendering means the content is in the HTML instead of waiting for extra JavaScript requests.
- Cut render-blocking work. Keep stylesheets small and avoid synchronous scripts in the head.
- Watch your server response time. A slow Time to First Byte "can make achieving a 2.5 second LCP challenging, or even impossible." Minimise redirects, and serve from a CDN with sensible caching where you can.
Where you host affects that last point more than any code change. If you are still choosing a host, this comparison of deployment options covers the trade-offs.
INP: responding when someone interacts
INP assesses a page's overall responsiveness by observing the latency of the clicks, taps and key presses a visitor makes. Hovering, scrolling and zooming are not counted.
Each interaction has three phases:
- Input delay: time before your event handler starts, usually because the main thread is busy with something else.
- Processing duration: time your handlers take to run.
- Presentation delay: time for the browser to calculate layout and paint the next frame.
The fixes from web.dev's INP guide map onto those phases:
- Break up long tasks. If a click handler does a lot of work, split it so the browser can paint in between. The guide suggests yielding with a timeout so the remaining work runs in a new task, and makes a useful point: "Yielding indiscriminately is better than not yielding at all."
- Do only the visual update first. Keep the handler to the logic needed for the next frame, such as showing a spinner or toggling a menu, and defer analytics, saving and other background work.
- Avoid layout thrashing. Writing styles and then immediately reading layout values in the same task forces the browser to calculate layout synchronously.
- Keep the DOM small. web.dev notes that "large DOMs do require more work to render than small DOMs". Long lists and tables are the usual culprits; render what is visible.
For a first app, the common INP problem is a form or filter that recalculates everything on every keystroke. Test your busiest interaction on a mid-range phone, not your development machine.
CLS: stopping the layout from jumping
CLS adds up unexpected layout shifts. It does not total every shift across the whole visit. It groups shifts into "session windows" with less than a second between shifts and a maximum of five seconds per window, and reports the largest burst. Shifts that happen within 500 milliseconds of a user's input are flagged as having recent input, so a menu opening because someone tapped it does not count against you.
The common causes are images and videos without known dimensions, ads or widgets that resize themselves, web fonts that render a different size from their fallback, and content inserted above what is already on screen. The fixes from web.dev's CLS guide:
- Always set width and height on images and videos. If you cannot, reserve the space with the CSS aspect-ratio property.
- Reserve space for anything that loads late. Banners, embeds and ad slots should have their space held in the initial layout. If the slot ends up empty, show a placeholder rather than collapsing it.
- Tame web fonts. The guide notes that font-display set to optional can avoid a re-layout, and the size-adjust descriptor narrows the size difference between the fallback and the web font.
- Animate with transforms. Use transform scale and translate rather than animating width, height, top or left.
- Stay eligible for the back/forward cache. The guide recommends this to keep CLS low when people navigate back and forward.
A cookie banner that pushes the whole page down a second after load is a classic first-app CLS bug. Overlay it instead of inserting it into the flow.
A short routine for your first app
Before launch:
- Run Lighthouse on your main pages, in mobile mode, and fix anything it flags for LCP and CLS.
- Identify the LCP element on each key page and confirm it is in the HTML, not lazy-loaded, and marked as high priority.
- Check that every image has dimensions and every late-loading element has reserved space.
- Click through your heaviest interactions with DevTools open and look for long tasks.
- After launch, check Search Console's Core Web Vitals report once you have traffic, and treat its numbers as the real score.
These checks sit naturally next to the other launch tasks in the pre-launch checklist for generated code. If you are working through the rest of your launch, web accessibility basics covers the other set of checks a first app most often misses.
Sources
- Web Vitals (web.dev)
- Largest Contentful Paint (web.dev)
- Optimize Largest Contentful Paint (web.dev)
- Interaction to Next Paint (web.dev)
- Optimize Interaction to Next Paint (web.dev)
- Cumulative Layout Shift (web.dev)
- Optimize Cumulative Layout Shift (web.dev)
- Understanding Core Web Vitals and Google search results (Google Search Central)



