`useReportWebVitals` (LCP) Intermittently Not Reporting When User is Idle
Link to the code that reproduces this issue
https://github.com/samtbg/userReportWebVitalsMissingLCP
To Reproduce
- Clone the linked repository and run the necessary package install command:
npm install --legacy-peer-deps
- Start the development server:
npm run dev
-
Navigate to http://localhost:3000.
-
Open the browser DevTools Console and clear the logs.
Test 1 (Failure Scenario - Idle User) This demonstrates the missing LCP report:
Reload the page.
Wait 12 seconds without moving or clicking the mouse.
Result: The console will show the "10-second timer fired" message. The OTel flush runs, and the log will confirm TTFB and FCP were sent, but LCP is missing.
Test 2 (Success Scenario - Interactive User) This demonstrates the required workaround:
Reload the page.
Wait 5 seconds (the LCP element is visible).
Click once anywhere on the white space. The console should show the LCP metric recorded immediately after the click.
Wait for the 10-second timer to fire (5 more seconds).
Result: The console will successfully show TTFB, FCP, and LCP being sent.
Current vs. Expected behavior
Current Behavior (Observable in the Console):
Test 1 (Failure): The console immediately logs the recording of TTFB and FCP. At approximately 10 seconds, the console logs the "10-second timer fired" and the OTel flush runs. The final flush log shows that LCP is missing from the buffer. The useReportWebVitals callback for LCP never executes.
Test 2 (Success): When a click on the page occurs, LCP is recorded and reported in the console
Expected Behaviour
The LCP is recorded/reported irregardless of user interaction
Provide environment information
From the reproduction:
Operating System:
Platform: linux
Arch: x64
Version: #88-Ubuntu SMP PREEMPT_DYNAMIC Sat Oct 11 09:28:41 UTC 2025
Available memory (MB): 39737
Available CPU cores: 8
Binaries:
Node: 20.11.0
npm: 11.4.2
Yarn: 1.22.22
pnpm: N/A
Relevant Packages:
next: 16.0.2-canary.24 // Latest available version is detected (16.0.2-canary.24).
eslint-config-next: N/A
react: 18.3.1
react-dom: 18.3.1
typescript: 5.4.5
Next.js Config:
output: N/A
However we're seeing this behaviour in our NextJS apps (which have a wide range of versions)
Which area(s) are affected? (Select all that apply)
Performance
Which stage(s) are affected? (Select all that apply)
next dev (local)
Additional context
No response
Fix: LCP Reporting for Idle Users
Problem
The useReportWebVitals hook was intermittently not reporting LCP (Largest Contentful Paint) metric when users were idle. According to issue #86291, LCP was only reported after user interaction (click, mouse movement, etc.), but not when users remained inactive on the page.
Root Cause
The web-vitals library doesn't finalize LCP metric until certain events occur. For idle users who don't interact with the page, the LCP metric never gets finalized and reported, leading to incomplete performance data.
Solution
Added event handlers for visibilitychange and pagehide events to finalize and report LCP metric when:
- The page visibility state changes to
hidden - The page is being unloaded (pagehide event)
Implementation Details
-
LCP Tracking: Uses
performance.getEntriesByType('largest-contentful-paint')to get the last LCP entry - Finalization Logic: When page becomes hidden or unloads, extracts the LCP value from the performance entry
- Duplicate Prevention: Tracks if LCP was already reported via the normal web-vitals flow to prevent duplicate reporting
-
Metric Construction: Creates a proper
Metricobject with:-
renderTimeorloadTimefrom the LCP entry (preferred) - Falls back to
startTimeif renderTime/loadTime are not available - Proper rating calculation (good/needs-improvement/poor)
- Normalized navigation type
-
Code Changes
File: packages/next/src/client/web-vitals.ts
- Added
lcpReportedRefto track if LCP was already reported - Added
finalizeLCP()function that:- Checks if LCP was already reported
- Gets the last LCP entry from Performance API
- Constructs and reports the LCP metric
- Added event listeners for
visibilitychangeandpagehide - Wrapped the web-vitals callback to track when LCP is reported normally
Testing
Manual Testing
-
Idle User Scenario:
# Start dev server npm run dev # Navigate to a page with useReportWebVitals # Wait 10+ seconds without interacting # Check console/logs - LCP should be reported when page becomes hidden -
Interactive User Scenario (should still work):
# Navigate to page # Interact with page (click, scroll) # LCP should be reported normally via web-vitals
Automated Testing
File: test/e2e/app-dir/app/useReportWebVitals.test.ts
Added test case: should report LCP for idle users when page becomes hidden
The test:
- Sets up a page with
useReportWebVitals - Waits for page to load
- Simulates
visibilitychangeevent withhiddenstate - Verifies that LCP metric was reported
Run the test:
npm test -- test/e2e/app-dir/app/useReportWebVitals.test.ts
Test Scenarios Covered
- ✅ LCP reported for idle users when page becomes hidden
- ✅ LCP reported on pagehide event
- ✅ No duplicate reporting when LCP is already reported normally
- ✅ Existing functionality (interactive users) still works
Files Changed
-
packages/next/src/client/web-vitals.ts- Main implementation -
test/e2e/app-dir/app/useReportWebVitals.test.ts- Test coverage
Related Issue
Fixes #86291: useReportWebVitals (LCP) Intermittently Not Reporting When User is Idle
😍 Thank you so much for the detailed response! We can change our implementation to push metrics on those events!
Do you know if we're waiting on an official release for the MR you raised before we can test it out?
Thanks, Sam