SPA [CLS Issues]

305 views
Skip to first unread message

Helios

unread,
Jun 29, 2021, 3:56:41 AM6/29/21
to web-vitals-feedback
Hey all! I would really appreciate it if you could assist with some guidance. We have an SPA (I can send you in private a URL if needed) and we've been trying to improve the CLS scores of it for quite a while now. However, no matter what we've been trying/fixing, the scores continue to be quite bad. None of our category pages are currently passing.. 

As of now, my concern is the following:

  • That we're incurring a severe CLS penalty when navigating between page types (i.e. category page vs product). 
  • That we are sometimes missing the 500ms window to shift content and while I've seen some situations where that happens, I wasn't able to reproduce this consistently at all. 
  • Forcing interactions with the page itself, while there are a few shifts (they are so minor) that the Chrome extension (version 1.0.1) is not picking up anything problematic (it remains in the green).
  • I'm also concerned over the fact that the back/forward buttons of the browser are not flagged for the hadRecentInput algorithm. As a lot of our traffic comes from Android, could this be a significant factor that is suppressing the pages? 
Any advice/guidance would help! Thank you!

Nicolás Peña Moreno

unread,
Jun 29, 2021, 10:56:17 AM6/29/21
to Helios, Michal Mocny, web-vitals-feedback
+Michal Mocny would usually reply to this but he's off this week (he may reply next week). I can reply to some of it though

On Tue, Jun 29, 2021 at 3:56 AM Helios <dr.ang...@gmail.com> wrote:
Hey all! I would really appreciate it if you could assist with some guidance. We have an SPA (I can send you in private a URL if needed) and we've been trying to improve the CLS scores of it for quite a while now. However, no matter what we've been trying/fixing, the scores continue to be quite bad. None of our category pages are currently passing.. 

Worth keeping in mind that the CLS definition has changed, so just be sure that you're looking at the latest one, which is more lenient for SPAs: https://web.dev/evolving-cls/
 

As of now, my concern is the following:

  • That we're incurring a severe CLS penalty when navigating between page types (i.e. category page vs product). 
  • That we are sometimes missing the 500ms window to shift content and while I've seen some situations where that happens, I wasn't able to reproduce this consistently at all. 
  • Forcing interactions with the page itself, while there are a few shifts (they are so minor) that the Chrome extension (version 1.0.1) is not picking up anything problematic (it remains in the green).
Also be sure to check for shifts in iframes, which could be missed by the extension. You'd need to register an observer on the iframe (or use some tool that gathers data from iframes).
 
  • I'm also concerned over the fact that the back/forward buttons of the browser are not flagged for the hadRecentInput algorithm. As a lot of our traffic comes from Android, could this be a significant factor that is suppressing the pages? 
Any advice/guidance would help! Thank you!

--
You received this message because you are subscribed to the Google Groups "web-vitals-feedback" group.
To unsubscribe from this group and stop receiving emails from it, send an email to web-vitals-feed...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/web-vitals-feedback/7225e852-82ac-43e5-8e92-33d6690a1eafn%40googlegroups.com.

Helios

unread,
Jul 1, 2021, 6:06:55 AM7/1/21
to web-vitals-feedback
Hi Nicolas,

Thank you very much for trying to assist with this! To clarify on some of your feedback:

  • Yes, we're aware of the change for SPAs. We've been tracking the shifts both through the core web vitals JS library and through observers we've setup. We haven't relied only on the Chrome extension. iFrames do not seem to be the problem though. However, I've been checking the new chrome extension v1.0.1 and I noticed the below. 
    • Let's say I'm on page A. The page A generates a random ID as this was tied in to the initial load of the page: v2-1625132965467-3738962238993. 
    • However, If I navigate across other category pages, page B, page C, page D, the CLS shifts that get seen are attributed to the same ID as above, despite me being on a completely different page? 
    • So, I'm seeing a scenario where the CLS is good till it meets a functionality that is not on a completely different page type. 
  • Is the scenario I'm describing above an expected behaviour? For an SPA, how do you know to generate a new unique ID for a completely different page? Do you know what it's tied to as it can be load, of course. Am I understand that there's an ID generated with an initial load and that is used till the end of the user's session?
Thanks a lot for your help with this!

Michal Mocny

unread,
Jul 6, 2021, 11:20:14 AM7/6/21
to Helios, Philip Walton, web-vitals-feedback
On Thu, Jul 1, 2021 at 6:06 AM Helios <dr.ang...@gmail.com> wrote:
Hi Nicolas,

Thank you very much for trying to assist with this! To clarify on some of your feedback:

  • Yes, we're aware of the change for SPAs. We've been tracking the shifts both through the core web vitals JS library and through observers we've setup. We haven't relied only on the Chrome extension. iFrames do not seem to be the problem though. However, I've been checking the new chrome extension v1.0.1 and I noticed the below. 
    • Let's say I'm on page A. The page A generates a random ID as this was tied in to the initial load of the page: v2-1625132965467-3738962238993. 
    • However, If I navigate across other category pages, page B, page C, page D, the CLS shifts that get seen are attributed to the same ID as above, despite me being on a completely different page? 
    • So, I'm seeing a scenario where the CLS is good till it meets a functionality that is not on a completely different page type. 
  • Is the scenario I'm describing above an expected behaviour? For an SPA, how do you know to generate a new unique ID for a completely different page? Do you know what it's tied to as it can be load, of course. Am I understand that there's an ID generated with an initial load and that is used till the end of the user's session?
Indeed, this is the current expected behaviour.  The 2021 definition of CLS expects that there will not be any ~5s window of time that exceeds the CLS threshold.  This includes the whole timeline from page load to page unload.

SPA page transitions are not typically considered "unexpected" layout shifts, so long as they happen within the 500ms exclusion window.  It is often not possible to fully load the next route (i.e. a category page) within this time, but it should be possible to unload the current UI, or even prepare a skeleton for the incoming transition.  It sounds like you already know this, and it seems you expect this to be working well most of the time already.

It is indeed not ideal that a poor CLS score caused on any route along the page timeline is attributed up to the initial page load, rather than to the route itself (category page) that was the more immediate cause of the shifts.  We have several ongoing projects to help improve this attribution for SPA-- but at the moment there is currently no simple standard way to do this, which works correctly for the full range of site designs.  (I'm sure there is a strategy that would work well for your case, but we need to consider all cases.  Consider for example pages which only transition a small portion of the UI during a route change, and the CLS happens in the shared/common portion of UI.  Or sites which do route changes many times a second as the user infinitely scrolls visual content.)


My 2cents: CLS, like all vitals, is measured at the 75th %ile of results across user reports from field data.  Your original email claims you think it is the 500ms window which is occasionally missed that is the root of your poor CLS, but that you think this happens rarely.  Perhaps it is not so rare for real users in the field?  It sounds like you are doing some local lab testing to try and replicate the issue... but you may have better device and network conditions than your field users.  Have you looked into CrUX field data, or, your own field measurements, to see if your hypothesis is correct?  And, while we measure CLS for the full page lifecycle, your own field analytics can (and should!) keep track of the route changes that led to the high CLS scores to make this type of attribution easier.

+Philip Walton for some pointers there, if those would be useful.

Good Luck!
 
Thanks a lot for your help with this!

On Tuesday, June 29, 2021 at 5:56:17 PM UTC+3 Nicolás Peña Moreno wrote:
+Michal Mocny would usually reply to this but he's off this week (he may reply next week). I can reply to some of it though

On Tue, Jun 29, 2021 at 3:56 AM Helios <dr.ang...@gmail.com> wrote:
Hey all! I would really appreciate it if you could assist with some guidance. We have an SPA (I can send you in private a URL if needed) and we've been trying to improve the CLS scores of it for quite a while now. However, no matter what we've been trying/fixing, the scores continue to be quite bad. None of our category pages are currently passing.. 

Worth keeping in mind that the CLS definition has changed, so just be sure that you're looking at the latest one, which is more lenient for SPAs: https://web.dev/evolving-cls/
 

As of now, my concern is the following:

  • That we're incurring a severe CLS penalty when navigating between page types (i.e. category page vs product). 
  • That we are sometimes missing the 500ms window to shift content and while I've seen some situations where that happens, I wasn't able to reproduce this consistently at all. 
  • Forcing interactions with the page itself, while there are a few shifts (they are so minor) that the Chrome extension (version 1.0.1) is not picking up anything problematic (it remains in the green).
Also be sure to check for shifts in iframes, which could be missed by the extension. You'd need to register an observer on the iframe (or use some tool that gathers data from iframes).
 
  • I'm also concerned over the fact that the back/forward buttons of the browser are not flagged for the hadRecentInput algorithm. As a lot of our traffic comes from Android, could this be a significant factor that is suppressing the pages? 
Any advice/guidance would help! Thank you!

--
You received this message because you are subscribed to the Google Groups "web-vitals-feedback" group.
To unsubscribe from this group and stop receiving emails from it, send an email to web-vitals-feed...@googlegroups.com.
To view this discussion on the web visit https://groups.google.com/d/msgid/web-vitals-feedback/7225e852-82ac-43e5-8e92-33d6690a1eafn%40googlegroups.com.

--
You received this message because you are subscribed to the Google Groups "web-vitals-feedback" group.
To unsubscribe from this group and stop receiving emails from it, send an email to web-vitals-feed...@googlegroups.com.

Philip Walton

unread,
Jul 7, 2021, 3:30:44 PM7/7/21
to Michal Mocny, Helios, web-vitals-feedback
On Tue, Jul 6, 2021 at 8:20 AM Michal Mocny <mmo...@google.com> wrote:

On Thu, Jul 1, 2021 at 6:06 AM Helios <dr.ang...@gmail.com> wrote:
Hi Nicolas,

Thank you very much for trying to assist with this! To clarify on some of your feedback:

  • Yes, we're aware of the change for SPAs. We've been tracking the shifts both through the core web vitals JS library and through observers we've setup. We haven't relied only on the Chrome extension. iFrames do not seem to be the problem though. However, I've been checking the new chrome extension v1.0.1 and I noticed the below. 
    • Let's say I'm on page A. The page A generates a random ID as this was tied in to the initial load of the page: v2-1625132965467-3738962238993. 
    • However, If I navigate across other category pages, page B, page C, page D, the CLS shifts that get seen are attributed to the same ID as above, despite me being on a completely different page? 
    • So, I'm seeing a scenario where the CLS is good till it meets a functionality that is not on a completely different page type. 
  • Is the scenario I'm describing above an expected behaviour? For an SPA, how do you know to generate a new unique ID for a completely different page? Do you know what it's tied to as it can be load, of course. Am I understand that there's an ID generated with an initial load and that is used till the end of the user's session?
Indeed, this is the current expected behaviour.  The 2021 definition of CLS expects that there will not be any ~5s window of time that exceeds the CLS threshold.  This includes the whole timeline from page load to page unload.

SPA page transitions are not typically considered "unexpected" layout shifts, so long as they happen within the 500ms exclusion window.  It is often not possible to fully load the next route (i.e. a category page) within this time, but it should be possible to unload the current UI, or even prepare a skeleton for the incoming transition.  It sounds like you already know this, and it seems you expect this to be working well most of the time already.

It is indeed not ideal that a poor CLS score caused on any route along the page timeline is attributed up to the initial page load, rather than to the route itself (category page) that was the more immediate cause of the shifts.  We have several ongoing projects to help improve this attribution for SPA-- but at the moment there is currently no simple standard way to do this, which works correctly for the full range of site designs.  (I'm sure there is a strategy that would work well for your case, but we need to consider all cases.  Consider for example pages which only transition a small portion of the UI during a route change, and the CLS happens in the shared/common portion of UI.  Or sites which do route changes many times a second as the user infinitely scrolls visual content.)

My 2cents: CLS, like all vitals, is measured at the 75th %ile of results across user reports from field data.  Your original email claims you think it is the 500ms window which is occasionally missed that is the root of your poor CLS, but that you think this happens rarely.  Perhaps it is not so rare for real users in the field?  It sounds like you are doing some local lab testing to try and replicate the issue... but you may have better device and network conditions than your field users.  Have you looked into CrUX field data, or, your own field measurements, to see if your hypothesis is correct?  And, while we measure CLS for the full page lifecycle, your own field analytics can (and should!) keep track of the route changes that led to the high CLS scores to make this type of attribution easier.

Yes, we definitely recommend that SPAs capture the specific page URL the shifts occurred on.

Since the web-vitals JS library does not (by default) report CLS until the page is backgrounded or unloaded, it can be a bit tricky to figure out which page was active after the fact.

The easiest way to do that is to keep a mapping of all history changes along with the timestamp of each change (e.g. any time `history.pushState()` is called). Then, when CLS is reported you can compare the timestamps of the associated entries with the timestamps of your history change map and determine what URL was active when the shift(s) occurred. In cases where the shifts span a URL change, it might be worth capturing that information as well.

Helios

unread,
Jul 14, 2021, 4:07:04 AM7/14/21
to web-vitals-feedback
Hey Michal & Philip,

Thanks a million for the details and help you've provided! Based on the discussions here, we've figured out what the problem was:
  • As we wanted to prioritize page types based on their business importance, we've taken a more granular approach in fixing CLS. i.e. revenue driving pages were prioritised over other page types
  • However, as we are an SPA and the CLS is actually attributed to the page where the initial load was, despite us fixing quite a few issues on our main pages, we're not seeing any impact on them due to user navigation and due to the fact users can encounter page types that haven't been fully fixed for CLS issues. 
  • As such, our main pages that were fixed and almost have a 0 CLS score get penalised depending on how users navigate and what page types they encounter through navigation. i.e. there are page types where we have some CSR dependencies that still cause large shifts.
I've added the details above in case someone else encounters the same issue. Not sure if this limitation on SPAs and the fact that the uniqueID generation is tied to load/unload events is very clear in the CLS documentation, but I'd recommend adding a few more points on this topic or at least expanding on the subject a bit. We'll look into improving our tracking based on Philip's recommendation. 

Thanks again for the help!


Helios

unread,
Aug 11, 2021, 8:12:04 AM8/11/21
to web-vitals-feedback

Hi all,

I just wanted to thank you again for the help with the above situation as it's made things considerably clearer. However, I do have a follow up question. On the same website (SPA) as above, we have the following situation:

  • A user lands on a search page. 
  • On the same page, if a user accesses the menu, the entire viewport is switched to an overlaid menu where they can use all kinds of filters to select the next page in their navigation - location, specific-vertical criteria, etc. 
  • That said, even changing a location, without actually hitting the button to actually send them to that page, incurs a penalty with the overlaid menu in front. 
  • It looks like despite the fact that nothing changes in the user's viewport due to the filters menu, the CLS algorithm sees the changes that are occurring behind it and adds penalties for that. We are changing the page in the background, which, again, is not visible to users.
As CLS should measure visible changes for users, I wanted to inquire if this is also intended or if this is a bug. I understand that the 500ms rule would circumvent this if it were respected, but this is a way of bypassing the problem, not addressing the fact that there's a CLS penalty while in the viewport nothing changes. 

I'd appreciate your help/feedback on this one as well ! Thank you! 

Michal Mocny

unread,
Aug 16, 2021, 10:14:12 AM8/16/21
to Helios, web-vitals-feedback
On Wed, Aug 11, 2021 at 8:12 AM Helios <dr.ang...@gmail.com> wrote:

Hi all,

I just wanted to thank you again for the help with the above situation as it's made things considerably clearer.

Thanks for this feedback!
 
However, I do have a follow up question. On the same website (SPA) as above, we have the following situation:

  • A user lands on a search page. 
  • On the same page, if a user accesses the menu, the entire viewport is switched to an overlaid menu where they can use all kinds of filters to select the next page in their navigation - location, specific-vertical criteria, etc. 
  • That said, even changing a location, without actually hitting the button to actually send them to that page, incurs a penalty with the overlaid menu in front. 
  • It looks like despite the fact that nothing changes in the user's viewport due to the filters menu, the CLS algorithm sees the changes that are occurring behind it and adds penalties for that. We are changing the page in the background, which, again, is not visible to users.
As CLS should measure visible changes for users, I wanted to inquire if this is also intended or if this is a bug. I understand that the 500ms rule would circumvent this if it were respected, but this is a way of bypassing the problem, not addressing the fact that there's a CLS penalty while in the viewport nothing changes. 

If I understand correctly, there is a real layout shift happening on the page but in a way which is visually obscured to the user (perhaps due to z-ordering) and you correctly expect it to be ignored?  Indeed, the layout-shift detection algorithm cannot detect all types of visual obscurity.  Many attributes on the unstable nodes are considered (visibility, opacity, coordinates/viewport/scroll/clip, etc), but there are many other ways content can be obscured.  If you could create a simple repro example, we could help identify if this is a known constraint, or a bug.

If the content is 100% visually obscured-- could you just delay the layout changes until the overlay menu is closed?

Reply all
Reply to author
Forward
0 new messages