Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(packages/sui-react-web-vitals): improve LCP and INP reporting #1618

Merged
merged 14 commits into from
Aug 8, 2023
Merged
Changes from all 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
80 changes: 59 additions & 21 deletions packages/sui-react-web-vitals/src/index.js
Original file line number Diff line number Diff line change
@@ -1,19 +1,30 @@
import {useContext, useEffect, useRef} from 'react'

import PropTypes from 'prop-types'
import * as reporter from 'web-vitals'
import * as reporter from 'web-vitals/attribution'

import SUIContext from '@s-ui/react-context'
import useMount from '@s-ui/react-hooks/lib/useMount/index.js'
import {useRouter} from '@s-ui/react-router'

export const METRICS = {
TTFB: 'TTFB',
LCP: 'LCP',
CLS: 'CLS',
FCP: 'FCP',
FID: 'FID',
INP: 'INP',
FCP: 'FCP'
LCP: 'LCP',
TTFB: 'TTFB'
}

const DEFAULT_METRICS_REPORTING_ALL_CHANGES = [METRICS.LCP, METRICS.INP]

const DEFAULT_CWV_THRESHOLDS = {
[METRICS.CLS]: 100,
[METRICS.FCP]: 1800,
[METRICS.FID]: 100,
[METRICS.INP]: 200,
[METRICS.LCP]: 2500,
[METRICS.TTFB]: 800
}

export const DEVICE_TYPES = {
Expand All @@ -27,11 +38,13 @@ const getNormalizedPathname = pathname => {
}

export default function WebVitalsReporter({
metrics = Object.values(METRICS),
pathnames,
children,
deviceType,
metrics = Object.values(METRICS),
metricsAllChanges = DEFAULT_METRICS_REPORTING_ALL_CHANGES,
onReport,
children
pathnames,
thresholds = DEFAULT_CWV_THRESHOLDS
}) {
const {logger, browser} = useContext(SUIContext)
const router = useRouter()
Expand All @@ -58,17 +71,32 @@ export default function WebVitalsReporter({
return deviceType || browser?.deviceType
}

const handleReport = ({name, value}) => {
const handleAllChanges = ({attribution, name, value}) => {
const amount = name === METRICS.CLS ? value * 1000 : value
const pathname = getPathname()
const isExcluded =
!pathname || (Array.isArray(pathnames) && !pathnames.includes(pathname))

if (isExcluded || !logger?.log || amount < thresholds[name]) return

logger.log(
JSON.stringify({
name: `cwv.${name.toLowerCase()}`,
amount,
...attribution
})
)
}

const handleChange = ({name, value}) => {
const onReport = onReportRef.current
const pathname = getPathname()
const routeid = getRouteid()
const type = getDeviceType()
const isExcluded =
!pathname || (Array.isArray(pathnames) && !pathnames.includes(pathname))

if (isExcluded) {
return
}
if (isExcluded) return

if (onReport) {
onReport({
Expand All @@ -81,9 +109,7 @@ export default function WebVitalsReporter({
return
}

if (!logger?.distribution) {
return
}
if (!logger?.distribution) return

const amount = name === METRICS.CLS ? value * 1000 : value

Expand Down Expand Up @@ -120,7 +146,9 @@ export default function WebVitalsReporter({
}

metrics.forEach(metric => {
reporter[`on${metric}`](handleReport)
reporter[`on${metric}`](handleChange)
if (DEFAULT_METRICS_REPORTING_ALL_CHANGES.includes(metric))
reporter[`on${metric}`](handleAllChanges, {reportAllChanges: true})
})
})

Expand All @@ -129,23 +157,33 @@ export default function WebVitalsReporter({

WebVitalsReporter.propTypes = {
/**
* An optional array of core web vitals. Choose between: TTFB, LCP, FID, CLS and INP. Defaults to all.
* An optional children node
*/
metrics: PropTypes.arrayOf(PropTypes.oneOf(Object.values(METRICS))),
children: PropTypes.node,
/**
* An optional string to identify the device type. Choose between: desktop, tablet and mobile
*/
deviceType: PropTypes.oneOf(Object.values(DEVICE_TYPES)),
/**
* An optional array of pathnames that you want to track
* An optional array of core web vitals. Choose between: TTFB, LCP, FID, CLS and INP. Defaults to all.
*/
pathnames: PropTypes.arrayOf(PropTypes.string),
metrics: PropTypes.arrayOf(PropTypes.oneOf(Object.values(METRICS))),
/**
* An optional array of core web vitals that will report on all changes. Choose between: TTFB, LCP, FID, CLS and INP. Defaults to LCP and INP.
*/
metricsAllChanges: PropTypes.arrayOf(PropTypes.oneOf(Object.values(METRICS))),
/**
* An optional callback to be used to track core web vitals
*/
onReport: PropTypes.func,
/**
* An optional children node
* An optional array of pathnames that you want to track
*/
pathnames: PropTypes.arrayOf(PropTypes.string),
/**
* An object with METRICS as keys and thresholds as values
* Thresholds by default are those above which Google considers the page as "needs improvement"
* Lower thresholds could be set for fine-tuning, higher thresholds could be set for less noise when reporting all changes
*/
children: PropTypes.node
thresholds: PropTypes.object
}
Loading