diff --git a/packages/next/client/index.tsx b/packages/next/client/index.tsx index a7a4b069f7b2cf9..43fae5b351d4bfc 100644 --- a/packages/next/client/index.tsx +++ b/packages/next/client/index.tsx @@ -462,10 +462,6 @@ function renderReactElement(reactEl: JSX.Element, domEl: HTMLElement) { if (isInitialRender) { ReactDOM.hydrate(reactEl, domEl, markHydrateComplete) isInitialRender = false - - if (onPerfEntry && ST) { - measureWebVitals(onPerfEntry) - } } else { ReactDOM.render(reactEl, domEl, markRenderComplete) } @@ -744,5 +740,10 @@ function Root({ } }, []) } + // We should ask to measure the Web Vitals after rendering completes so we + // don't cause any hydration delay: + React.useEffect(() => { + measureWebVitals(onPerfEntry) + }, []) return children as React.ReactElement } diff --git a/packages/next/client/performance-relayer.ts b/packages/next/client/performance-relayer.ts index f1b43fa7e10e7c8..1a2dfe1cb899c6d 100644 --- a/packages/next/client/performance-relayer.ts +++ b/packages/next/client/performance-relayer.ts @@ -4,13 +4,32 @@ import { getFID, getLCP, getTTFB, + Metric, ReportHandler, } from 'web-vitals' -export default (onPerfEntry: ReportHandler) => { - getCLS(onPerfEntry) - getFID(onPerfEntry) - getFCP(onPerfEntry) - getLCP(onPerfEntry) - getTTFB(onPerfEntry) +let isRegistered = false +let userReportHandler: ReportHandler | undefined + +function onReport(metric: Metric) { + if (userReportHandler) { + userReportHandler(metric) + } +} + +export default (onPerfEntry?: ReportHandler) => { + // Update function if it changes: + userReportHandler = onPerfEntry + + // Only register listeners once: + if (isRegistered) { + return + } + isRegistered = true + + getCLS(onReport) + getFID(onReport) + getFCP(onReport) + getLCP(onReport) + getTTFB(onReport) } diff --git a/test/integration/build-output/test/index.test.js b/test/integration/build-output/test/index.test.js index 92bf58533a36762..a1c77da767cb072 100644 --- a/test/integration/build-output/test/index.test.js +++ b/test/integration/build-output/test/index.test.js @@ -104,7 +104,7 @@ describe('Build Output', () => { expect(parseFloat(err404FirstLoad) - 64.2).toBeLessThanOrEqual(0) expect(err404FirstLoad.endsWith('kB')).toBe(true) - expect(parseFloat(sharedByAll) - 60.7).toBeLessThanOrEqual(0) + expect(parseFloat(sharedByAll) - 60.8).toBeLessThanOrEqual(0) expect(sharedByAll.endsWith('kB')).toBe(true) if (_appSize.endsWith('kB')) {