ampproject / ampproject/amphtml

amp-analytics: Allow for optional variables to support Core Web Vitals

Open
#35,663 1 comment 2 reactions 0 assignees View on GitHub
Stale Type: Feature Request
Dominant language
JavaScript
Stars
14.9k
Forks
4.1k
PR merge metrics
No merged PRs in 30d

Description

### Description

Hi,

In `amp-analytics` today, any variables that you request on a beacon will "hold" the beacon from being sent until all of the variables are resolved:

https://amp.dev/documentation/components/amp-analytics/#vars

> Variables are resolved asynchronously and can delay the request until they are fulfilled. For example, some metrics such as Cumulative Layout Shift and Largest Contentful Paint are calculated after the page is hidden. For First Input Delay, it is resolved after the user interacts with the page. For this reason these metrics might not be suitable for use with all triggers (for example, on timer or visible).

Unfortunately, "resolving" of some of the variables may depend on whether that event happens, or even whether the browser supports that feature.

For example, we (Akamai mPulse) are interested in capturing some of the Core Web Vitals such as LCP, CLS and FID. When we went to add these variables to our "primary" beacon, it was pointed out that these CWV metrics shouldn't be placed along regular page analytics data:

* [PR: pdate amp-analytics provider mpulse.json to add Core Web Vitals metrics](https://github.com/ampproject/amphtml/pull/33553)

We closed this PR after it was pointed out that:

> CLS & LCP is only calculated when you move out of the page or go to another tab.
> FID is calculated only after the user clicks on the browser.

So our first approach was to simply:

* Change our main analytics beacon from sending at `visible` to `hidden` but also including CLS and LCP
* Send an optional "FID" beacon if input happens

This resulted in two possible beacons:

```
{
"requests": {
"onvisible": "https://${beacon_url}?...&c.cls=${cumulativeLayoutShift}&pt.lcp=${largestContentfulPaint}&pt.fcp=${firstContentfulPaint}",

"onvisible-fid": "https://${beacon_url}?...&et.fid=${firstInputDelay}"
},
"triggers": {
"onvisible-fid": {
"on": "visible",
"request": "onvisible-fid"
}
}
}
```

Unfortunately we've found this doesn't work reliably, because if the _browser_ doesn't support CLS/LCP (e.g. non-Chromium browsers), then the main beacon doesn't get sent either.

So instead, we're having to have the browser send four separate beacons, one for the main page data and one for each CWV:

```
{
"requests": {
"base_api": "https://${beacon_url}?...",
"fid": "${base_api}&et.fid=${firstInputDelay}",
"cls": "${base_api}&c.cls=${cumulativeLayoutShift}",
"lcp": "${base_api}&pt.lcp=${largestContentfulPaint}"
},
"triggers": {
"fid": {
"on": "visible",
"request": "fid"
},
"cls": {
"on": "visible",
"request": "cls"
},
"lcp": {
"on": "visible",
"request": "lcp"
}
}
}
```

While this works, it's certainly not ideal:

* Sending a single beacon should be more efficient for the browser than (up to) 4 requests
* There's a lot of duplicate information being sent to ensure the beacons can be linked together
* On the server-side of beacon collection, having a single beacon is far more ideal than (up to) 4 separate beacons.
* For example, we have to either store each request separately in the database (taking much more storage) or "stitch" them together in memory first (adding complexity and server load)

What I'm wondering is if there would be a way to specify "optional" parameters for beacons -- if the parameter doesn't resolve (e.g. FID doesn't happen) or the browser doesn't support it (e.g. CLS or LCP), then that value would resolve and send an empty parameter.

Maybe you'd be able to specify a parameter is optional with double curlys `${{parameterName}}` or something.

Then, the above request could be simplified back to a single beacon:

```
{
"requests": {
"onhidden": "https://${beacon_url}?...&c.cls=${{cumulativeLayoutShift}}&pt.lcp=${{largestContentfulPaint}}&pt.fcp=${{firstContentfulPaint}}&et.fid=${{firstInputDelay}}",
},
"triggers": {
"onhidden": {
"on": "hidden",
"request": "onhidden"
}
}
}
```

The beacon would be sent at `hidden`, and would only contain values for FID (if it happened) and CLS/LCP if they were supported by the browser too. The important thing is it would always be sent, even if the browser didn't support them.

Or maybe `extraUrlParams` gets a `value` and `optional` field:

```
"extraUrlParams": {
"c.cls": {
value: "${cumulativeLayoutShift}",
optional: true
}
}
```

Thoughts?

### Alternatives Considered

I've seen some other similar requests in the past, e.g.:

* https://github.com/ampproject/amphtml/issues/34623#issuecomment-852173240
* https://github.com/ampproject/amphtml/issues/30810#issuecomment-721216509

### Additional Context

_No response_

Contributor guide

Open the contributing guide

Research direction

Start with amp-analytics variable resolution and the vars documentation, then compare the beacon configurations and alternatives described in the issue. Done means defining a supported optional-variable behavior that still sends one beacon when Core Web Vitals are unresolved or unsupported, while including values when available.

Written by the indexing model from the issue text.

Assessment

Tech stack
javascript
Domain
analytics, frontend
Issue type
Feature
Difficulty
5/5
Estimated time
Over a week
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
35/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.