Skip to content

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 filter view_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_change that 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_shift is 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:

  1. first_byte is high: prioritize checking server response, redirects, CDN, geography, and network;
  2. first_byte is normal but first_contentful_paint is high: prioritize checking render-blocking CSS, JavaScript, and client-side initialization;
  3. first_contentful_paint is normal but largest_contentful_paint is high: prioritize checking whether the LCP element's resource was discovered late, downloaded slowly, or rendered with delay;
  4. loading_time is 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, and scripts in R::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:

  1. view_path_group: first identify the affected pages;
  2. version: determine if a new version caused a regression;
  3. browser, browser_version_major: determine browser compatibility or capability differences;
  4. device, screen_size: distinguish device performance and layout differences;
  5. network_type: distinguish Wi-Fi, cellular, and unreachable networks;
  6. 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:

  1. 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;
  2. FCP missing candidate: a finished first-load View that stays for more than 3 seconds, but first_contentful_paint does 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_change in 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_byte is also very high: prioritize checking the server, CDN, network, and redirects;
  • first_byte is 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 the session_id or view_id to 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:

  1. P75 trends and good rates for LCP, INP, and CLS;
  2. P75 trends for FCP, first_byte, and loading_time;
  3. Top 20 slow pages sorted by view_path_group;
  4. Pre- and post-release trends compared by version;
  5. Tail experience broken down by browser, device, network_type, and geography;
  6. Correlation charts of view_error_count, view_long_task_count, frustration_count with performance metrics;
  7. 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.

Feedback

Is this page helpful?