-
Notifications
You must be signed in to change notification settings - Fork 414
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
Web Vital Metrics for Single Page Applications #119
Comments
Had the same question, the docs need some context for SPAs (or any app with history-based navigation) For example, there should be a way to manually reset the values based on a trigger or event. |
I had wondered whether subsequent page loads with an SPA are even (or should be) considered wrt web-vitals? Either way I'd be interested in seeing some guidance on how to handle web-vital measurement for SPAs |
Unfortunately, accounting for SPA route transitions in Core Web Vitals metrics is not currently possible. There are a few primary reasons for this, both technical reasons and practical reasons. The technical reason is that the Web APIs that measure page load specific Core Web Vitals metrics (LCP and FID) do not re-emit entries after a SPA route change occurs. For FID we could use the same polyfill we use in the case of a bfcache restore, but for LCP we could not, since route changes typically involve loading additional content, and polyfilling LCP to handle those cases would be near impossible (and it would certainly affect performance). Also, as you point out, in the case of CLS we could reset the metric value to 0 (as we do for a bfcache restore), but this is where the practical issues come into play: what counts as an SPA route change?
While I understand it's probably quite easy for you to determine what should be considered a "logical page load" for your specific app. Making that determination for any given web page is pretty much impossible. Finally, and perhaps the most challenging of the practical issues is that both Google Chrome and Google Search are creating incentives around these metrics, which means it's absolutely critical that they can't be easily gamed. If we consider every call to For example, you could easily imagine some code like this: // Create a new "page load" any time CLS gets close to crossing the 0.1 threshold...
If (cls > 0.09) {
history.pushState({...}, document.title, location += '&addendum=1)
} Similarly, developers might try calling Hopefully this helps explain why Core Web Vitals metrics currently don't consider SPA route transitions. But rest assured we are aware of this problem and looking into solutions. Here are two concrete examples:
|
Thanks for the quick response! Above is really good explanation on why current route change identification (within SPA) alone is not sufficient for the web vitals that must not be gamed, but at the same time very critical that it's measured accurately, since this would impact page ranking in May 2021 unless the timeline is changed. It is really great that the team is actively looking for the solution and the official documentations around SPA have started to surface recently. |
Thanks for the response! I would just add that or part of that to the docs for the package. Even if vitals don't apply, there are natural questions that get asked: "how does this apply to SPAs?" A few sentences would answer that cleanly. |
FYI: we're working on some content related to SPAs for web.dev/vitals. Once that's published we can link to it from this repo. |
Wow, I've spent months pondering why google search console flags so many of our SPA's pages as having poor CLS but lab data (lighthouse) and performance traces (Chrome dev tools) suggest it's very good. On first load we generally see I think the layout shifts which are being accumulated across SPA navigations are those caused by a successful network response after navigation because, like many SPAs, on click we show a blank screen with a loading indicator while fetching the data, and then when the data arrives we render it to screen causing a layout -- if the page data being navigated to is already in memory then I've observed the CLS isn't reported, I guess because the layout is the direct result of a click handler -- https://www.paradeworld.com/ In the meantime we're going to only report the first CLS value reported by |
Hey @richardscarrott, see the post on how we plan to update CLS that I mentioned above to address your concern about how the value just keeps increasing the longer the user is on the page. As for the the technique of showing a loading spinner and then filling in the content once the network request completes, this may still be counted as layout shifts—if it's the case that elements on the page are shifting as new content is being added. I'd recommend updating your SPA transition logic to either:
|
Hi @philipwalton thanks for the advice. I had a bit of a think about your last point re: hiding the content while it's loading and realised I had initially thought the layout shift was merely counting the layout of the new content rendered to screen but of course it's our footer which get's shifted further down the page. It wasn't immediately obvious to me because we do render the footer offscreen while loading by reserving the height of the viewport; this was done specifically to prevent the footer flashing into view. e.g. And I guess, although unlikely imo, a user could scroll the footer into view during the loading period and then genuinely experience a layout shift. It seems layout shifts don't discriminate against things inside vs outside the viewport 🤔. I tested with the footer hidden while loading and as you've suggested this solves the issue so we're seeing almost 0 CLS reports during the page lifecycle. Of course, we still have the issue of the now rarer and slight layout shifts accumulating over the SPA session but I'll give that article another read and look forward to hearing about improvements on this. UPDATE: So I had a proper read of the CLS docs and of course it does attempt to only consider layout shifts which are in the viewport, however it seems to get this wrong with our footer. Adding UPDATE2: So there obviously had to be a reason it was 20px specifically and I think it's because we have a negative margin on a child element in our footer to compensate for some third party styles, so although visually it didn't appear to be in the viewport it's bounding box did creep in by 20px. |
I am just getting started with measuring of CLS and LCP for my React based SPA and came across web vitals module and this thread. |
This is expected. LCP is only measured at page load, and this is intentional for the reasons I mentioned above. Also, the reason you're seeing LCP logged to the console after you interact is because that is the point when LCP observation stop waiting to see if a larger element gets added to the DOM (we can't log the value before that point).
CLS is only logged when the page is backgrounded or unloaded, so if you switch tabs you should see it logged. You'll also see it logged if you check the preserve log option in devtools and reload the page (it will log CLS for the previous page). For both LCP and CLS, if you want to log the metric value every time it changes, you can do that with the
I suppose it depends on what you consider "expected", but based on what you've described, it sounds like it's working exactly as intended. If you were expecting all the Web Vitals metrics to re-emit after a route change in an SPA, that is not expected to happen. |
@richardscarrott thanks for the update.
This library is just collecting what the underlying Layout Instability API is reporting. If you think this behavior is confusing or wrong, I'd recommend filing an issue on the spec highlighting your use case, and it will be discussed there. |
@philipwalton even after the new update I can still see the same page id when I navigate btw pages in a SPA. This means that the cumulative CLS is summed to the initial page. On a flow: search page => category page all the CLSs have the same id. Is this expected? |
Yes, this is expected. Until we have more reliable ways to detect user-initiated SPA transitions (or a dedicated web APIs to measure them) then Core Web Vitals metrics will need to be attributed to the URL was that present at the time the page was loaded. We recognize that this makes debugging quite awkward in cases like the one you described: where the problematic layout shifts occur after an SPA transition to a new URL. If you're measuring your Core Web Vitals with an analytics tool, we recommend collecting both the URL at the time of the shift and the URL that the page was originally loaded at for debugging purposes. |
Just to add to this conversation, this is an email I had sent previously to the Chrome Speed team in Feb 2021:
|
I think I'm mostly curious about whether we should even be tracking web vitals one page transitions within SPAs. When google uses vitals scores, is it accounting for internal navigation within a site, or is it simply comparing the scores from the initial page load of each individual page as an entry point? Clearly, the entry point load is always going to have worse scores than any internal transition, as there is some framework boilerplate that you need on the initial load of a page that won't happen if you transition to that same page after initially entering the app on another page (ie, scores for going directly to example.com/about would be worse than scores computed transitioning to /about after already loading the example.com homepage, right?). Correct me if I'm wrong, but my understanding is that google measures the vitals on the page from the entry point perspective (ie someone clicks a link from google directly to a page) and thus measuring metrics of internal page transitions are just going to conflate your aggregations to a much more favorable number than what google is actually ranking you for, no? |
To provide an update to everyone, we recently published a post on web.dev with answers to common questions about how SPA architectures affect Core Web Vitals: https://web.dev/vitals-spa-faq/ The post includes a section re: what is Google doing to address the SPA issue with some specific plans. Once there's more progress on those proposals I'll update this library to incorporate any changes here. |
I think the development of Web Vitals should depend on the need for web performance optimization and not be constrained by Google search, although Web Vitals is dominated by Google. I'm sorry if i caused offense. |
I do agree with @zhouqicf I must say core web vital is cool and can help to measure whether the website is cool enough or not |
Please note there is experimental support of this in the |
I used the web Vitals extension Google browser plugin to observe SPA single page routing switching and found that it can collect changes in CLS and LCP indicators, but calling the onCLS and onLCP methods with web Vitals does not work |
Yes we have not released a version of the extension with this experimental branch yet because it is still experimental and subject to change. |
I'm very sorry! I probably didn't express the issue clearly . I installed the web Vitals plugin in Chrome browser; The name of the plugin is' web vitals extension' the browser plugin link https://github.com/GoogleChrome/web-vitals-extension My question is:The browser plugin can detect SPA router change values, why can't I use web Vitals in the code to obtain these change values . Examples: step 1. pageload show metricsstep 2. SPA router change show metrics |
The web vitals extension always shows the current URL when the Heads Up Display (HUD) is opened. This has not changed and was always the case. Perhaps we should change this, to make it more obvious it's based on the initial page. LCP for an SPA is measured across all the soft navigations. However LCP is finalised when a click happens, so normally you wouldn't see an LCP change (assuming you do a click to initiate the soft navigation?). However, if there was an automatic change in SPA route, without an interaction, then yes the LCP would still continue to take the latest one if it is bigger. Chrome will also measure this as a new LCP but report it back based on the initial URL. The experimental branch, however, "resets" LCP so it is logged on the second and subsequent SPA page even if it is not bigger. So that's why it's different. |
Coming here after I realize that INP and CLS are fired on window unload (and after reading web vitals faq) Just want to add on our specific use case. The specific issue for our specific use case:
Like many have said, would be great to see them fired as well on route (history?) change, that way we would be able to use the metrics for the same route to calculate our custom scoring system for that particular route. This is why it's ideal for all the metrics to get emitted in the same route. |
@philipwalton Hi I was wondering is there any update on this? Is there any metrics that is accurate on SPA from the improvements? I'm particularly interested in FID and INP |
This is the most recent update: https://developer.chrome.com/docs/web-platform/soft-navigations-experiment |
@philipwalton thanks for you comments here, I understand why web-vitals package or chrome by itself can't identify what is a SPA page transition that should trigger a reset. Having said that, it would be amazing if the web-vitals package itself had an API that can be used when the website owner knows it's a SPA page transition and it's now time to reset cumulative metrics and re-emit FID on this page load. I hope that makes sense to add to the existing package or create a separate package which will add this support. |
In React Single page Application(SPA), First Input Delay and Largest Contentful Paint are only measured once on Initial Load. Additionally, Cumulative Layout Shift does not reset to 0 throughout the session. This means CLS could reach to very high value , if user continues to navigate though logical pages like homepage -> search -> Product Listing -> Product Page -> Basket -> Checkout and so on.
Based on current implementation, pageshow event resets CLS to 0 and it also captures FID and LCP for subsequent page loads.
In React Single Page Application, Could we consider route change event for the new logical page load and reset CLS to 0. This would also enable us to capture FID and LCP on every logical page of the application.
If Web Vital Metrics would be considered for page ranking, the proposed implementation would give React SPA fair comparison.
The text was updated successfully, but these errors were encountered: