Web performance
How Relay reads real-user Core Web Vitals from the Chrome UX Report, and why field data changes slowly.
Field data shows real Chrome users over the last 28 days, so a change needs about 4 weeks to show fully. Relay never says that a speed change caused a ranking change.
Field data and lab data
- Field data comes from the Chrome UX Report (CrUX). It is the 75th percentile of real Chrome users on phones or desktops, over 28 days. Google uses field data for Core Web Vitals.
- Lab data comes from Lighthouse in a site audit. It is one simulated load on one device. It is useful to find a cause, but it can differ from what real users see.
Relay shows three field metrics, with Google's "good" limits at the 75th percentile:
| Metric | Good |
|---|---|
| Largest Contentful Paint (LCP) | 2,500 ms or less |
| Interaction to Next Paint (INP) | 200 ms or less |
| Cumulative Layout Shift (CLS) | 0.1 or less |
Weekly field data refresh
The Field data refresh job in Automation runs once a week. It reads CrUX for:
- your site's origin, from your Search Console property;
- the key pages in your project context;
- the pages with the most organic clicks in the last 28 days.
It reads phones and desktops, at most 70 requests for each project. CrUX is free. A page with too few Chrome users has no page-level data. For those pages, Relay uses the origin and says so.
Performance issues
When you run detection on the Opportunities page, Relay groups your pages by URL pattern, for example all pages like /blog/:slug. It looks only at pages with organic clicks and at your key pages.
- Relay measures each group on its most-visited page that has field data. It uses phone data first, because Google indexes the mobile page.
- When a metric is above "good", Relay creates one Performance issue for that group and metric.
- Groups with no page-level data share one item for each metric, measured on your whole site's origin.
Use the Performance group of the type filter to see them. Relay orders them by how far the metric is above "good", the organic clicks of the pages, and the role or GA4 conversions of the pages.
Causes and tickets
Relay reads the latest site audit's Lighthouse reports for the pages in the group, and names the causes that match the failing metric:
| Cause | Suggested change |
|---|---|
| The LCP image loads lazily | Remove loading="lazy" from the largest image above the fold, and give it the priority hint. |
| The LCP image is not prioritized | Give the largest image above the fold the priority hint. |
| Images without a size | Add width and height to each image. |
| Third-party scripts block the page | Load analytics, chat and tag manager scripts after the page. |
| Render-blocking CSS or JavaScript | Inline the CSS that the first screen needs, and load the rest without blocking. |
| Large or old-format images | Serve images at the size they are shown, in WebP or AVIF. |
| Unused JavaScript | Split or remove JavaScript that the page does not use. |
| Slow server response | Cache the page or make the server respond faster. |
When no audit ran Lighthouse on those pages, the item says so. Run a site audit with Lighthouse turned on to find the cause. Approve an item to copy its ticket or create a GitHub issue. The ticket lists the field data, the lab value and each cause with its change.
Fix in code
If your project has a connected GitHub repository, a page-level item can have a Fix in code button for three causes. Each fix changes one attribute of one element:
- Load the LCP image first. Relay finds the image that the Lighthouse report names, in the page file or the components it imports. It removes
loading="lazy"and adds the priority prop that your Next.js version supports:priorityin Next.js 13 to 15,preloadfrom Next.js 16. It works onnext/imageelements only, and needs Next.js inpackage.json. - Add the image size. For an
<img>element whose file is inpublic/, Relay reads the first 64 KB of the file, finds the width and height in its header, and adds them. It reads PNG, JPEG, WebP and AVIF files. It does not changefillimages. - Load analytics or chat scripts later. Relay sets
strategy="lazyOnload"on onenext/scriptelement in the page or the root layout. It changes only scripts from a reviewed list of analytics, chat and tag manager hosts, such as Google Tag Manager, Hotjar and Intercom. Tracking may then start later, so very short visits may not be counted.
If Relay finds no element, or more than one, it stops and tells you to fix it by hand. Nothing is written to GitHub until you approve the change.
Lab check
A page-level item has a Lab check section. Run a lab check runs Lighthouse 3 times on the page from a phone, and keeps the median of each metric. The button shows the cost first: each run is one paid Lighthouse request.
Run one check before the change ships and one after it. When the item is marked Implemented, Relay compares the newest full check before that day with the newest one after it, and shows the change in percent. Lighthouse cannot measure Interaction to Next Paint without a person, so for an INP item the lab check shows Total Blocking Time instead.
Field result after a change
Phase 1 changes ship through a ticket, so Relay measures from the day a person marks the item Implemented.
- On the item, select Check the field result. Relay reads the weekly history of the measured page from the Chrome UX Report. It reads once an hour at most, and the request is free.
- Relay compares the median of the 4 weekly points before the change with the median of the 4 points that start 4 weeks after it. Each point covers 28 days, so the points after the change hold only days after it.
- The result is ready about 7 weeks after the change. Until then, Relay shows the date to check again.
The labels are:
- Improved: the value became good, or it improved by 15% or more.
- Worse: the value stopped being good, or it got 15% or more slower.
- No clear change: anything else.
Other changes, seasons and traffic shifts also move field data. Relay shows the result without a claim about what caused it.
Web Vitals snippet for pages without field data
Pages with few Chrome users have no Chrome UX Report data. For them, you can add an optional snippet to your Next.js site. It sends each page load's LCP, INP and CLS, with its rating, to your own Google Analytics. Nothing goes to Relay.
- Filter the Opportunities page by Performance issue, and select Set up the snippet.
- Add the component to your app, and render it once in the root layout.
- In Google Analytics, create an event-scoped custom dimension for the event parameter
metric_rating.
After a day, the same card shows your busiest pages with the share of loads rated good for each metric, over 28 days. A metric passes at 75% or more, which is the same test as a good 75th percentile. Relay uses this test because Google Analytics cannot report a percentile of an event value.
Self-hosting
Set CRUX_API_KEY to a Google API key with the Chrome UX Report API turned on. Without the key, the job skips and says that field data access is not configured.