Web Page Performance and User Experience Analysis¶
The Web RUM SDK associates the loading, rendering, interaction, layout shifts, errors, resources, and long tasks from real user visits with the same view_id. After data is reported to Guance, you can first use Core Web Vitals to assess the overall experience, then narrow down the affected scope by page, version, browser, device, and network, and finally drill down into errors, resources, long tasks, and Session Replay along the view_id.
All field names in this article come from the current data structure reported by the Web RUM SDK. For the complete field list, see Web Application Data Collection.
Clarify the Statistical Scope Before Analysis¶
Distinguish First Load From Route Changes¶
view_loading_type distinguishes two types of Views:
initial_load: the browser opens or refreshes the page for the first time;route_change: a route change occurs in a single-page application (SPA).
The current SDK only collects first_byte, first_contentful_paint, largest_contentful_paint, first_input_delay, and navigation timing fields on initial_load Views. loading_time, interaction_to_next_paint, cumulative_layout_shift, and View-related counts apply to both initial_load and route_change.
Therefore:
- When analyzing first-screen, white-screen candidates, FCP, LCP, and
TTFB, you must filterview_loading_type = "initial_load"; - When analyzing SPA route performance, prioritize
loading_time,interaction_to_next_paint,cumulative_layout_shift, errors, and long tasks; - Do not treat records in
route_changethat lack FCP or LCP as anomalies.
Use P75 Instead of Only Averages¶
Core Web Vitals recommends evaluating using the 75th percentile (P75) of page visit samples, and observing mobile and desktop separately. Meeting the P75 threshold means that at least 75% of page visits fall within the target range. Averages are easily diluted by many fast samples and do not represent the real experience of slow users.
It is recommended to observe simultaneously:
- P50: typical user experience;
- P75: the primary judgment metric for Core Web Vitals;
- P90 or P95: tail user experience, such as slow network or low-performance devices;
- Good rate: the proportion of Views within the "good" threshold out of valid samples;
- Sample size: avoid judging page or version quality based on a small number of samples.
For metric definitions, thresholds, and P75 calculation methods, refer to web.dev: Web Vitals and Best practices for measuring Web Vitals in the field.
Note the Units¶
- Except for
cumulative_layout_shift, all time metrics listed in this article are stored in nanoseconds (ns) in the data; 1s = 1,000,000,000ns,1ms = 1,000,000ns;cumulative_layout_shiftis a unitless fraction; do not apply time conversion;- DQL thresholds must use the original storage unit. For example, an LCP of 2.5 seconds should be written as
2500000000.
Core Experience Metrics¶
Core Web Vitals¶
| Experience Dimension | SDK Field | Valid View | Good | Needs Improvement | Poor | Locator Field |
|---|---|---|---|---|---|---|
| Loading Performance (LCP) | largest_contentful_paint |
initial_load |
≤ 2.5s | > 2.5s and ≤ 4s | > 4s | largest_contentful_paint_element_selector |
| Interaction Responsiveness (INP) | interaction_to_next_paint |
initial_load, route_change |
≤ 200ms | > 200ms and ≤ 500ms | > 500ms | interaction_to_next_paint_target_selector |
| Visual Stability (CLS) | cumulative_layout_shift |
initial_load, route_change |
≤ 0.1 | > 0.1 and ≤ 0.25 | > 0.25 | cumulative_layout_shift_target_selector |
The thresholds for LCP, INP, and CLS are based on LCP, INP, and CLS respectively.
LCP: When Users See the Main Content¶
largest_contentful_paint indicates the time when the largest image, text block, or video in the viewport completes rendering. The SDK also reports largest_contentful_paint_element_selector, which is used to locate the page element that caused the LCP.
When LCP is poor, combine the following fields to determine where the time is spent:
first_byteis high: prioritize checking server response, redirects, CDN, geography, and network;first_byteis normal butfirst_contentful_paintis high: prioritize checking render-blocking CSS, JavaScript, and client-side initialization;first_contentful_paintis normal butlargest_contentful_paintis high: prioritize checking whether the LCP element's resource was discovered late, downloaded slowly, or rendered with delay;loading_timeis still significantly higher than LCP: the main content is already visible, but network requests or DOM changes are still occurring.
INP: Whether the Page Can Respond to Interactions in Time¶
interaction_to_next_paint measures the full delay from an interaction input, through event handling, to the next frame paint. The SDK continuously observes interactions within a View and reports a value close to the worst interaction, while also identifying the corresponding element via interaction_to_next_paint_target_selector.
When INP is poor, focus on correlating:
view_long_task_count: whether there are tasks blocking the main thread for more than 50ms;view_error_count: whether script errors occurred during the interaction;frustration_count: whether frustration behaviors such as repeated clicks, dead clicks, or error clicks appeared;duration,blocking_duration, andscriptsinR::long_task: locate the blocking time and related scripts.
Views without user interaction may not have interaction_to_next_paint. When calculating INP, only use samples where this field exists.
CLS: Whether Page Content Moves Unexpectedly¶
cumulative_layout_shift measures the unexpected displacement of visible page elements, and cumulative_layout_shift_target_selector points to the element causing the most impact.
When CLS is poor, common directions to check include:
- Images, videos, ads, or
iframes without explicit dimensions; - Asynchronous content inserted above existing content;
- Web font swaps causing text size changes;
- Animations implemented through properties that trigger layout.
First-Load Supporting Metrics¶
| SDK Field | Meaning | Suggested Use |
|---|---|---|
first_byte |
Time from navigation start to receiving the first byte of the document response | Determine if the server, network, redirects, or CDN slow down subsequent rendering |
first_contentful_paint |
Time when the first text, image, SVG, or non-white Canvas is painted | Measures when the user first sees content; also the primary field for white-screen candidate analysis |
dom_interactive |
Time when the document parser finishes parsing | Determine if HTML and synchronous scripts delay the DOM's interactive state |
dom_content_loaded |
Time when DOMContentLoaded is completed |
Determine the completion time of DOM construction and deferred scripts |
dom_complete |
Time when the page and all subresources have completed | Determine if the full document lifecycle is too long |
load_event |
Time when the load event completes |
Determine when all dependent resources are fully loaded |
first_paint_time |
Calculated by the current SDK as responseEnd - fetchStart |
Observe the duration of the document response phase; do not equate it to FCP |
time_to_interactive |
Calculated by the current SDK as domInteractive - fetchStart |
Observe the time until the DOM reaches an interactive state; do not confuse with lab TTI |
dom_ready |
Calculated by the current SDK as domContentLoadedEventEnd - fetchStart |
Observe the duration until DOM Ready is complete |
resource_load_time |
Calculated by the current SDK as loadEventStart - domContentLoadedEventEnd |
Observe the time spent waiting for dependent resources after DOM Ready |
dom |
Calculated by the current SDK as domComplete - domInteractive |
Observe the time from DOM being interactive to fully complete |
For FCP, refer to web.dev: FCP: good is ≤ 1.8 seconds, poor is > 3 seconds. For first_byte, refer to web.dev: Time to First Byte: 0.8 seconds is usually good, > 1.8 seconds is usually poor; TTFB is not a Core Web Vital; it should be evaluated together with FCP and LCP.
Page Loading and Usage Experience Metrics¶
| SDK Field | Meaning | Questions It Answers |
|---|---|---|
loading_time |
The page stabilization time calculated by the SDK based on the load event, network requests, and DOM activity |
When does the page finish its main loading activity? Does the SPA route change continue to make requests or update DOM? |
time_spent |
The time the user stays on the current View | Does a slow page cause quick departure? Does an anomaly affect long usage? |
view_error_count |
Number of errors associated with the current View | Is the page unusable due to script errors? |
view_resource_count |
Number of resource requests associated with the current View | Does the page request too many resources? Does it need further resource waterfall analysis? |
view_long_task_count |
Number of long tasks associated with the current View | Is the main thread frequently blocked? |
view_action_count |
Number of user actions associated with the current View | Is the page actually being used? Is the lack of INP samples due to no interaction? |
frustration_count |
Number of frustration behaviors associated with the current View | Are users repeating actions due to unresponsiveness, no results, or errors? |
is_active |
Whether the View is still active | Filter out Views that have not ended and whose metrics may still be updated |
session_has_replay |
Whether the current session has associated Session Replay | Can you directly replay the scene of the anomalous user? |
loading_time is not equivalent to visual completion time. The page can first complete LCP, but background polling, API requests, or continuous DOM updates can make loading_time longer; conversely, a normal loading_time does not prove that the main content has been rendered correctly.
Establish a Page Performance Baseline¶
Step 1: View P75 for Each Page¶
The following queries are intended for dashboard charts and deliberately do not write fixed time ranges like [24h] or [1h] in the DQL. Omitting the time range causes the query to follow the time widget in the upper right of the dashboard; if you specify a time range directly in the DQL, its priority is higher than the dashboard time widget, making the chart query that fixed time period. For more information, see DQL Time Range Specification. Replace <APP_ID> with the application ID.
View LCP P75 by page:
R::view:(percentile(largest_contentful_paint, 75) AS p75_lcp) {app_id = "<APP_ID>", view_loading_type = "initial_load", largest_contentful_paint = exists()} BY view_path_group SORDER BY p75_lcp DESC SLIMIT 20
View INP P75 by page:
R::view:(percentile(interaction_to_next_paint, 75) AS p75_inp) {app_id = "<APP_ID>", interaction_to_next_paint = exists()} BY view_path_group SORDER BY p75_inp DESC SLIMIT 20
View CLS P75 by page:
R::view:(percentile(cumulative_layout_shift, 75) AS p75_cls) {app_id = "<APP_ID>", cumulative_layout_shift = exists()} BY view_path_group SORDER BY p75_cls DESC SLIMIT 20
Do not directly sum the three metrics into a single score. LCP, INP, and CLS represent loading, responsiveness, and stability respectively; each poor metric needs to be addressed separately.
Step 2: Observe the Good Rate¶
The following query calculates the proportion of valid Views where LCP does not exceed 2.5 seconds:
eval(100 * A / B, alias='LCP_good_rate', A="R::view:(count(view_id)) {app_id = '<APP_ID>', view_loading_type = 'initial_load', largest_contentful_paint <= 2500000000}", B="R::view:(count(view_id)) {app_id = '<APP_ID>', view_loading_type = 'initial_load', largest_contentful_paint = exists()}")
Similarly, you can replace the conditions with:
- INP good:
interaction_to_next_paint <= 200000000; - CLS good:
cumulative_layout_shift <= 0.1; - FCP good:
first_contentful_paint <= 1800000000.
The denominator must only count valid samples where the metric exists; otherwise, browsers that do not support the metric, Views without interaction, or Views that are left too early will distort the results.
Step 3: Break Down by Key Dimensions¶
It is recommended to use the following SDK fields for grouping or filtering in order:
view_path_group: first identify the affected pages;version: determine if a new version caused a regression;browser,browser_version_major: determine browser compatibility or capability differences;device,screen_size: distinguish device performance and layout differences;network_type: distinguish Wi-Fi, cellular, and unreachable networks;env,service: avoid mixing data from different environments or services.
For example, compare Core Web Vitals by version and page:
R::view:(percentile(largest_contentful_paint, 75) AS p75_lcp, percentile(interaction_to_next_paint, 75) AS p75_inp, percentile(cumulative_layout_shift, 75) AS p75_cls) {app_id = "<APP_ID>"} BY version, view_path_group SORDER BY p75_lcp DESC SLIMIT 50
Since LCP only exists in initial_load and INP may be missing due to no interaction, it is recommended in production dashboards to create separate charts for the three metrics and then use the same page, version, and device filter conditions for linked analysis.
White Screen and Long No-Content Analysis¶
White Screen Is Not a Direct SDK Metric¶
The current SDK does not report a field named white_screen. White screen can only be inferred by constructing candidate sets from already collected metrics, and then confirmed by combining errors, resources, long tasks, and Session Replay.
It is recommended to divide candidates into two levels:
- Slow FCP candidate:
first_contentful_paint > 3000000000. This means the user saw the first content only after more than 3 seconds. It is a relatively stable, statistical approximation of white-screen experience; - FCP missing candidate: a finished first-load View that stays for more than 3 seconds, but
first_contentful_paintdoes not exist. This rule can only be used for troubleshooting; it cannot be directly equated to white screen.
FCP missing can also be caused by:
- The current browser does not support the FCP collection capability used by the SDK;
- The page entered the background or was hidden before FCP was generated;
- The user closed the page prematurely;
- Data sampling, network reporting, or page lifecycle ends early;
- Incorrectly including
route_changein the analysis.
Therefore, the FCP missing rule must filter view_loading_type = "initial_load" and verify collection coverage by browser. For browser capability boundaries, see Browser Support.
Calculate the Slow FCP Candidate Ratio¶
eval(100 * A / B, alias='white_screen_candidate_rate', A="R::view:(count(view_id)) {app_id = '<APP_ID>', view_loading_type = 'initial_load', first_contentful_paint > 3000000000}", B="R::view:(count(view_id)) {app_id = '<APP_ID>', view_loading_type = 'initial_load', first_contentful_paint = exists()}")
This ratio represents "the proportion of valid FCP samples where the user saw content only after more than 3 seconds," not the actual white-screen rate confirmed by visual verification. It is recommended to further break down by view_path_group, version, browser, and network_type.
Find Slow FCP Views¶
R::view:(view_id, session_id, view_url, first_contentful_paint, first_byte, loading_time, view_error_count, view_resource_count, view_long_task_count, session_has_replay) {app_id = "<APP_ID>", view_loading_type = "initial_load", first_contentful_paint > 3000000000} ORDER BY first_contentful_paint DESC LIMIT 100
Result interpretation:
first_byteis also very high: prioritize checking the server, CDN, network, and redirects;first_byteis normal but FCP is very high: prioritize checking render-blocking resources, client-side rendering, and synchronous JavaScript;view_error_count > 0: prioritize checking the error stack to confirm if initialization was interrupted;view_long_task_count > 0: check if the main thread was blocked by long tasks before the first paint;session_has_replay = true: use thesession_idorview_idto open Session Replay and confirm what the user actually saw.
Find FCP Missing Candidates¶
R::view:(view_id, session_id, view_url, time_spent, view_error_count, view_resource_count, view_long_task_count, session_has_replay) {app_id = "<APP_ID>", view_loading_type = "initial_load", is_active = false, time_spent > 3000000000, first_contentful_paint != exists()} ORDER BY time_spent DESC LIMIT 100
First, exclude samples from browsers that do not support FCP, then combine with Session Replay and drill-down queries to confirm. Do not create a "real white-screen rate" alert solely based on missing FCP.
Drill Down to Root Cause Along view_id¶
Substitute the <VIEW_ID> from the candidate results into the following queries.
View script and runtime errors:
R::error:(error_type, error_source, error_message, error_stack) {app_id = "<APP_ID>", view_id = "<VIEW_ID>"} ORDER BY time ASC LIMIT 100
View failed or slow resources:
R::resource:(resource_url, resource_type, resource_status, duration, resource_ttfb, resource_trans) {app_id = "<APP_ID>", view_id = "<VIEW_ID>", resource_status >= 400} ORDER BY duration DESC LIMIT 100
If there are no HTTP errors but you still suspect resource slowness, you can remove resource_status >= 400 and then view the slowest resources by duration. A high resource_ttfb usually points to server or network waiting, while a high resource_trans usually points to content transfer time.
View long tasks:
R::long_task:(duration, blocking_duration, scripts) {app_id = "<APP_ID>", view_id = "<VIEW_ID>"} ORDER BY duration DESC LIMIT 100
duration indicates the total duration of the long task, blocking_duration indicates the time it blocked user input, and scripts can be used to locate the related scripts.
Diagnosis Paths for Common Experience Issues¶
| Phenomenon | First Check Field | Further Correlate | Common Directions |
|---|---|---|---|
| Page shows no content for a long time | first_contentful_paint, first_byte |
view_error_count, view_long_task_count, Resource |
Slow server, render blocking, initialization error, long tasks |
| Main content loads slowly | largest_contentful_paint |
largest_contentful_paint_element_selector, Resource |
LCP resource discovered late, download slow, rendering delay |
| Click does not respond | interaction_to_next_paint |
interaction_to_next_paint_target_selector, Long Task, Error |
Main thread blocked, event handling too heavy, script error |
| Page content jumps | cumulative_layout_shift |
cumulative_layout_shift_target_selector |
Missing dimensions, dynamic insertion, fonts and animations |
| Page keeps showing loading | loading_time |
Resource, DOM activity, Error | Request not finished, polling, continuous DOM updates, unhandled error state |
| User clicks repeatedly or clicks but no result | frustration_count |
Action, INP, Error, Session Replay | No visual feedback, dead clicks, error clicks, slow interaction |
| New version experience degrades | version + P75 |
view_path_group, browser, network_type |
Resource size, code path, compatibility, or release regression |
Dashboard and Alert Recommendations¶
It is recommended to create at least the following views:
- P75 trends and good rates for LCP, INP, and CLS;
- P75 trends for FCP,
first_byte, andloading_time; - Top 20 slow pages sorted by
view_path_group; - Pre- and post-release trends compared by
version; - Tail experience broken down by
browser,device,network_type, and geography; - Correlation charts of
view_error_count,view_long_task_count,frustration_countwith performance metrics; - Slow FCP candidate ratio and list of replayable samples.
For alerts, prioritize using P75 or the good rate, set a minimum sample size and consecutive trigger conditions to avoid false alarms from a small number of anomalous samples or short-term traffic fluctuations. It is recommended to start from the "poor" threshold of Core Web Vitals and then adjust based on the importance of business pages, historical baselines, and actual traffic.
Complete the Analysis in the Console¶
- In Real User Monitoring (RUM) > Analysis Dashboard > Web, view the overview, page performance, resources, and error analysis;
- In Real User Monitoring (RUM) > Explorer, filter raw Views by
view_id,session_id, page, version, and browser; - After entering a single View, view the associated Resource, Error, Long Task, Action, and Session Replay;
- When custom statistics are needed, use the DQL in this article to create dashboard charts or RUM metric detection.
For more console capabilities, see Analysis Dashboard, Explorer, and RUM Metric Detection.