Skip to content
Merged
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
159 changes: 80 additions & 79 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -100,7 +100,7 @@ To load the "attribution" build, change any `import` statements that reference `
+ import {onLCP, onINP, onCLS} from 'web-vitals/attribution';
```

Usage for each of the imported function is identical to the standard build, but when importing from the attribution build, the [`Metric`](#metric) object will contain an additional [`attribution`](#metricwithattribution) property.
Usage for each of the imported function is identical to the standard build, but when importing from the attribution build, the [metrics](#metrics) objects will contain an additional [`attribution`](#attribution) property.

See [Send attribution data](#send-attribution-data) for usage examples, and the [`attribution` reference](#attribution) for details on what values are added for each metric.

Expand Down Expand Up @@ -488,17 +488,70 @@ For guidance on how to collect and use real-user data to debug performance issue

## API

### Types:
### Types

#### `Metric`
#### Metrics

##### [`CLSMetric`](/src/types/cls.ts#:~:text=interface%20CLSMetric)

```ts
interface Metric {
/**
* The name of the metric (in acronym form).
*/
name: 'CLS' | 'FCP' | 'FID' | 'INP' | 'LCP' | 'TTFB';
interface CLSMetric extends Metric {
name: 'CLS';
entries: LayoutShift[];
}
```

##### [`FCPMetric`](/src/types/fcp.ts#:~:text=interface%20FCPMetric)

```ts
interface FCPMetric extends Metric {
name: 'FCP';
entries: PerformancePaintTiming[];
}
```

##### [`FIDMetric`](/src/types/fid.ts#:~:text=interface%20FIDMetric)

```ts
interface FIDMetric extends Metric {
name: 'FID';
entries: PerformanceEventTiming[];
}
```

##### [`INPMetric`](/src/types/inp.ts#:~:text=interface%20INPMetric)

```ts
interface INPMetric extends Metric {
name: 'INP';
entries: PerformanceEventTiming[];
}
```

##### [`LCPMetric`](/src/types/lcp.ts#:~:text=interface%20LCPMetric)

```ts
interface LCPMetric extends Metric {
name: 'LCP';
entries: LargestContentfulPaint[];
}
```

##### [`TTFBMetric`](/src/types/ttfb.ts#:~:text=interface%20TTFBMetric)

```ts
interface TTFBMetric extends Metric {
name: 'TTFB';
entries: PerformanceNavigationTiming[];
}
```

##### `Metric`

All metrics share the following properties:

```ts
interface Metric {
/**
* The current value of the metric.
*/
Expand Down Expand Up @@ -527,13 +580,6 @@ interface Metric {
*/
id: string;

/**
* Any performance entries relevant to the metric value calculation.
* The array may also be empty if the metric value was not based on any
* entries (e.g. a CLS value of 0 given no layout shifts).
*/
entries: (PerformanceEntry | LayoutShift)[];

/**
* The type of navigation.
*
Expand All @@ -556,39 +602,6 @@ interface Metric {
}
```

Metric-specific subclasses:

- [`CLSMetric`](/src/types/cls.ts#:~:text=interface%20CLSMetric)
- [`FCPMetric`](/src/types/fcp.ts#:~:text=interface%20FCPMetric)
- [`FIDMetric`](/src/types/fid.ts#:~:text=interface%20FIDMetric)
- [`INPMetric`](/src/types/inp.ts#:~:text=interface%20INPMetric)
- [`LCPMetric`](/src/types/lcp.ts#:~:text=interface%20LCPMetric)
- [`TTFBMetric`](/src/types/ttfb.ts#:~:text=interface%20TTFBMetric)

#### `MetricWithAttribution`

See the [attribution build](#attribution-build) section for details on how to use this feature.

```ts
interface MetricWithAttribution extends Metric {
/**
* An object containing potentially-helpful debugging information that
* can be sent along with the metric value for the current page visit in
* order to help identify issues happening to real-users in the field.
*/
attribution: {[key: string]: unknown};
}
```

Metric-specific subclasses:

- [`CLSMetricWithAttribution`](/src/types/cls.ts#:~:text=interface%20CLSMetricWithAttribution)
- [`FCPMetricWithAttribution`](/src/types/fcp.ts#:~:text=interface%20FCPMetricWithAttribution)
- [`FIDMetricWithAttribution`](/src/types/fid.ts#:~:text=interface%20FIDMetricWithAttribution)
- [`INPMetricWithAttribution`](/src/types/inp.ts#:~:text=interface%20INPMetricWithAttribution)
- [`LCPMetricWithAttribution`](/src/types/lcp.ts#:~:text=interface%20LCPMetricWithAttribution)
- [`TTFBMetricWithAttribution`](/src/types/ttfb.ts#:~:text=interface%20TTFBMetricWithAttribution)

#### `MetricRatingThresholds`

The thresholds of metric's "good", "needs improvement", and "poor" ratings.
Expand All @@ -604,28 +617,11 @@ The thresholds of metric's "good", "needs improvement", and "poor" ratings.
| > [1] | "poor" |

```ts
export type MetricRatingThresholds = [number, number];
type MetricRatingThresholds = [number, number];
```

_See also [Rating Thresholds](#rating-thresholds)._

#### `ReportCallback`

```ts
interface ReportCallback {
(metric: Metric): void;
}
```

Metric-specific subclasses:

- [`CLSReportCallback`](/src/types/cls.ts#:~:text=interface%20CLSReportCallback)
- [`FCPReportCallback`](/src/types/fcp.ts#:~:text=interface%20FCPReportCallback)
- [`FIDReportCallback`](/src/types/fid.ts#:~:text=interface%20FIDReportCallback)
- [`INPReportCallback`](/src/types/inp.ts#:~:text=interface%20INPReportCallback)
- [`LCPReportCallback`](/src/types/lcp.ts#:~:text=interface%20LCPReportCallback)
- [`TTFBReportCallback`](/src/types/ttfb.ts#:~:text=interface%20TTFBReportCallback)

#### `ReportOpts`

```ts
Expand Down Expand Up @@ -667,7 +663,7 @@ type LoadState =
#### `onCLS()`

```ts
type onCLS = (callback: CLSReportCallback, opts?: ReportOpts) => void;
function onCLS(callback: (metric: CLSMetric) => void, opts?: ReportOpts): void;
```

Calculates the [CLS](https://web.dev/articles/cls) value for the current page and calls the `callback` function once the value is ready to be reported, along with all `layout-shift` performance entries that were used in the metric value calculation. The reported value is a [double](https://heycam.github.io/webidl/#idl-double) (corresponding to a [layout shift score](https://web.dev/articles/cls#layout_shift_score)).
Expand All @@ -679,7 +675,7 @@ _**Important:** CLS should be continually monitored for changes throughout the e
#### `onFCP()`

```ts
type onFCP = (callback: FCPReportCallback, opts?: ReportOpts) => void;
function onFCP(callback: (metric: FCPMetric) => void, opts?: ReportOpts): void;
```

Calculates the [FCP](https://web.dev/articles/fcp) value for the current page and calls the `callback` function once the value is ready, along with the relevant `paint` performance entry used to determine the value. The reported value is a [`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp).
Expand All @@ -689,7 +685,7 @@ Calculates the [FCP](https://web.dev/articles/fcp) value for the current page an
_Deprecated and will be removed in next major release_

```ts
type onFID = (callback: FIDReportCallback, opts?: ReportOpts) => void;
function onFID(callback: (metric: FIDMetric) => void, opts?: ReportOpts): void;
```

Calculates the [FID](https://web.dev/articles/fid) value for the current page and calls the `callback` function once the value is ready, along with the relevant `first-input` performance entry used to determine the value. The reported value is a [`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp).
Expand All @@ -699,7 +695,7 @@ _**Important:** since FID is only reported after the user interacts with the pag
#### `onINP()`

```ts
type onINP = (callback: INPReportCallback, opts?: ReportOpts) => void;
function onINP(callback: (metric: INPMetric) => void, opts?: ReportOpts): void;
```

Calculates the [INP](https://web.dev/articles/inp) value for the current page and calls the `callback` function once the value is ready, along with the `event` performance entries reported for that interaction. The reported value is a [`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp).
Expand All @@ -713,7 +709,7 @@ _**Important:** INP should be continually monitored for changes throughout the e
#### `onLCP()`

```ts
type onLCP = (callback: LCPReportCallback, opts?: ReportOpts) => void;
function onLCP(callback: (metric: LCPMetric) => void, opts?: ReportOpts): void;
```

Calculates the [LCP](https://web.dev/articles/lcp) value for the current page and calls the `callback` function once the value is ready (along with the relevant `largest-contentful-paint` performance entry used to determine the value). The reported value is a [`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp).
Expand All @@ -723,7 +719,10 @@ If the `reportAllChanges` [configuration option](#reportopts) is set to `true`,
#### `onTTFB()`

```ts
type onTTFB = (callback: TTFBReportCallback, opts?: ReportOpts) => void;
function onTTFB(
callback: (metric: TTFBMetric) => void,
opts?: ReportOpts,
): void;
```

Calculates the [TTFB](https://web.dev/articles/ttfb) value for the current page and calls the `callback` function once the page has loaded, along with the relevant `navigation` performance entry used to determine the value. The reported value is a [`DOMHighResTimeStamp`](https://developer.mozilla.org/docs/Web/API/DOMHighResTimeStamp).
Expand Down Expand Up @@ -760,15 +759,17 @@ console.log(INPThresholds); // [ 200, 500 ]
console.log(LCPThresholds); // [ 2500, 4000 ]
```

_**Note:** It's typically not necessary (or recommended) to manually calculate metric value ratings using these thresholds. Use the [`Metric['rating']`](#metric) supplied by the [`ReportCallback`](#reportcallback) functions instead._
_**Note:** It's typically not necessary (or recommended) to manually calculate metric value ratings using these thresholds. Use the [`Metric['rating']`](#metric) instead._

### Attribution:

The following objects contain potentially-helpful debugging information that can be sent along with the metric values for the current page visit in order to help identify issues happening to real-users in the field.

When using the attribution build, these objects are found as an `attribution` property on each metric.

See the [attribution build](#attribution-build) section for details on how to use this feature.

#### CLS `attribution`:
#### [CLS `attribution`](/src/types/cls.ts#:~:text=interface%20CLSMetricWithAttribution)

```ts
interface CLSAttribution {
Expand Down Expand Up @@ -809,7 +810,7 @@ interface CLSAttribution {
}
```

#### FCP `attribution`:
#### [FCP `attribution`](/src/types/fcp.ts#:~:text=interface%20FCPMetricWithAttribution)

```ts
interface FCPAttribution {
Expand Down Expand Up @@ -841,7 +842,7 @@ interface FCPAttribution {
}
```

#### FID `attribution`:
#### [FID `attribution`](/src/types/fid.ts#:~:text=interface%20FIDMetricWithAttribution)

```ts
interface FIDAttribution {
Expand Down Expand Up @@ -873,7 +874,7 @@ interface FIDAttribution {
}
```

#### INP `attribution`:
#### [INP `attribution`](/src/types/inp.ts#:~:text=interface%20INPMetricWithAttribution)

```ts
interface INPAttribution {
Expand Down Expand Up @@ -964,7 +965,7 @@ interface INPAttribution {
}
```

#### LCP `attribution`:
#### [LCP `attribution`](/src/types/lcp.ts#:~:text=interface%20LCPMetricWithAttribution)

```ts
interface LCPAttribution {
Expand Down Expand Up @@ -1019,7 +1020,7 @@ interface LCPAttribution {
}
```

#### TTFB `attribution`:
#### [TTFB `attribution`](/src/types/ttfb.ts#:~:text=interface%20TTFBMetricWithAttribution)

```ts
export interface TTFBAttribution {
Expand Down
31 changes: 16 additions & 15 deletions src/attribution/onCLS.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,8 +18,7 @@ import {getLoadState} from '../lib/getLoadState.js';
import {getSelector} from '../lib/getSelector.js';
import {onCLS as unattributedOnCLS} from '../onCLS.js';
import {
CLSReportCallback,
CLSReportCallbackWithAttribution,
CLSAttribution,
CLSMetric,
CLSMetricWithAttribution,
ReportOpts,
Expand All @@ -33,26 +32,31 @@ const getLargestLayoutShiftSource = (sources: LayoutShiftAttribution[]) => {
return sources.find((s) => s.node && s.node.nodeType === 1) || sources[0];
};

const attributeCLS = (metric: CLSMetric): void => {
const attributeCLS = (metric: CLSMetric): CLSMetricWithAttribution => {
// Use an empty object if no other attribution has been set.
let attribution: CLSAttribution = {};

if (metric.entries.length) {
const largestEntry = getLargestLayoutShiftEntry(metric.entries);
if (largestEntry && largestEntry.sources && largestEntry.sources.length) {
const largestSource = getLargestLayoutShiftSource(largestEntry.sources);
if (largestSource) {
(metric as CLSMetricWithAttribution).attribution = {
attribution = {
largestShiftTarget: getSelector(largestSource.node),
largestShiftTime: largestEntry.startTime,
largestShiftValue: largestEntry.value,
largestShiftSource: largestSource,
largestShiftEntry: largestEntry,
loadState: getLoadState(largestEntry.startTime),
};
return;
}
}
}
// Set an empty object if no other attribution has been set.
(metric as CLSMetricWithAttribution).attribution = {};

// Cast to attribution metric so it can be populated.
const metricWithAttribution = metric as CLSMetricWithAttribution;
metricWithAttribution.attribution = attribution;
return metricWithAttribution;
};

/**
Expand All @@ -77,14 +81,11 @@ const attributeCLS = (metric: CLSMetric): void => {
* during the same page load._
*/
export const onCLS = (
onReport: CLSReportCallbackWithAttribution,
onReport: (metric: CLSMetricWithAttribution) => void,
opts?: ReportOpts,
) => {
unattributedOnCLS(
((metric: CLSMetricWithAttribution) => {
attributeCLS(metric);
onReport(metric);
}) as CLSReportCallback,
opts,
);
unattributedOnCLS((metric: CLSMetric) => {
const metricWithAttribution = attributeCLS(metric);
onReport(metricWithAttribution);
}, opts);
};
Loading