Skip to content

Dashboard loading state UX improvements#6480

Open
RobertJoonas wants to merge 3 commits into
masterfrom
dashboard-loading-states
Open

Dashboard loading state UX improvements#6480
RobertJoonas wants to merge 3 commits into
masterfrom
dashboard-loading-states

Conversation

@RobertJoonas

@RobertJoonas RobertJoonas commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Changes

  • Enable the fade-in animation when switching between IndexBreakdown tabs (e.g. Sources -> Channels) -- done by passing a key={selectedTabKey} to the IndexBreakdown components
    • Note: the animation does not trigger when changing dashboard state to something that has already been visited (i.e. the results are cached)
  • WorldMap component improvements
    • initital state (data is fetching) -- render loading spinner instead of blank world map without colors
    • wrap with LazyLoader like any other IndexBreakdown (fetches data only after scrolled into view)
    • add fade-in animation when transitioning from loading -> data (or showing cached results after tab change) -- basically do the same as IndexBreakdowns
    • code organization: separate SVG rendering into a new component (world-map-svg.tsx) that assumes the presence of data

Tests

  • This PR does not require tests

Changelog

  • This PR does not make a user-facing change

Documentation

  • This change does not need a documentation update

Dark mode

  • The UI has been tested both in dark and light mode

Also separates SVG rendering from loading state handling and adds a fade-in
animation to the map appearing (like all other IndexBreakdowns)
@github-actions

github-actions Bot commented Jul 2, 2026

Copy link
Copy Markdown
Preview environment👷🏼‍♀️🏗️
PR-6480

@RobertJoonas RobertJoonas requested a review from apata July 2, 2026 15:28

export type MetricLabel = { singular: string; plural: string }

export function WorldMapSvg({

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

question: Why must the world map flash in and out whenever the user is doing something on the dashboard? Is it to indicate we're actually working on their new selection? Can we find a cheaper, less disruptive way to indicate that? By cheaper, I mean less work for the browser. By less disruptive, I mean something that's changing less things on the dashboard at once. The map itself flashing in and out is, in my view, a step backward.

As I see it, the world map with country outlines is constant (at least in dashboard timescales). The previous implementation took pains to respect that and to just fade paint according to the current data.

With dummy.site where the autogenerated visitors are spread between very few locations, this fade wasn't very noticeable, but on real sites, it seems the variety of visitor locations guarantees a noticeable change in the map when filters are changed.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why must the world map flash in and out whenever the user is doing something on the dashboard? Is it to indicate we're actually working on their new selection?

Yeah, and also just trying to make the UX consistent. It feels strange when you apply a filter and the whole dashboard reacts to that change with loading spinners all around (including the graph container), but the map stays intact. Sometimes, there's not even any notable change in the colors and the report just looks frozen.

Can we find a cheaper, less disruptive way to indicate that? By cheaper, I mean less work for the browser. By less disruptive, I mean something that's changing less things on the dashboard at once. The map itself flashing in and out is, in my view, a step backward.

Yeah, I see how it feels a bit like stepping backwards. But perhaps for the sake of consistency, it's acceptable for the time being?

Letting the SVGs (both map and the main graph) stay mounted throughout the loading state is definitely an idea worth pursuing -- I can see the potential to make the loading state look really nice, but only if we get rid of the loading spinners altogether. For the rest of the dashboard (i.e. IndexBreakdowns) we should probably run some animations from old (placeholder) data to new, fresh data. There's also the question of the initial load where we'd most likely have to introduce some silhouettes. I believe @sanne-san had something sketched out already.

Doing all that sounds quite tricky and I don't think this will be worked on any time soon. Therefore proposing this small step backwards in pursuit of consistency.

Re: browser performance, I believe it used to work like this in the past for a long time without any issues. Do you see it as a blocker @apata?

@apata apata Jul 3, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Disruptive is subjective and I couldn't prove the performance difference, so I shouldn't say it's a blocker. It's something that fully caught my attention though.

(I tried to benchmark this env vs staging with browser performance APIs but failed: the envs didn't have as much as parity as I expected. There were differences in first paint performance, which this PR shouldn't much affect).

Even though I don't have concrete data to show, I just want us to keep in mind that any time we do relayout or fade or anything fancier (like scaling up / down animation), it's the user's device doing work. We want the work to be justified and proportional to the benefits we hope to achieve.

Also, the more we do, the more we are exposed to glitching from bugs in browsers. Maybe a minor matter, but it's an argument in favor of simplicity.

Re: browser performance, I believe it used to work like this in the past for a long time without any issues.

It seems "worked in the past without any issues" argument can be used to justify not changing the map loading logic as well. To me, it's a matter of choosing what we (or what we believe our users will) value more, slightly more consistent from report box to box, or slightly less visually disruptive.

On the subject of consistency, there's the time axis to consider as well. The map has worked without flashing in and out for years. Changing it (in any way) will cause an inconsistency with on the time axis. I suspect quite a few users don't actually like things changing.

Comment on lines +157 to +161
maxValue: number
dataByAlpha3Code: Map<string, CountryData>
metricLabel: MetricLabel
mode: UIMode
onCountryClick: (country: CountryData) => void

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nitpick, non-blocking: These 5 props pass through and the others are from API state to calculate whether to show loading screen. Can it be avoided by removing this intermediate component?
Also, mode can be accessed from context directly in WorldMapSVG, it's not used elsewhere.

Comment on lines +177 to +183
<div
className="relative flex justify-center items-center mt-4 w-full"
style={{
height: MAP_CONTAINER_HEIGHT,
maxWidth: MAP_CONTAINER_WIDTH
}}
>

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

suggestion, non-blocking: Because this wrapper is integral to rendering the map and tooltip correctly, I'd wrap it in WorldMapSvg

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants