Browser Progress Bar is an Anti-pattern

The user initiates a navigation, and the browser gets busy: it'll likely have to resolve a dozen DNS names, establish an even larger number of connections, and then dispatch one or more requests over each. In turn, for each request, it often does not know the response size (chunked transfers), and even when it does, it is still unable to reliably predict the download time due to variable network weather, server processing times, and so on. Finally, fetching and processing one resource might trigger an entire subtree of new requests.

Ok, so loading a page is complicated business, so what? Well, if there is no way to reliably predict how long the load might take, then why do so many browsers still use and show the progress bar? At best, the 0-100 indicator is a lie that misleads the user; worse, the success criteria is forcing developers to optimize for "onload time", which misses the progressive rendering experience that modern applications are aiming to deliver. Browser progress bars fail both the users and the developers; we can and should do better.

Indeterminate indicators in post-onload era

To be clear, progress indicators are vital to helping the user understand that an operation is in progress. The browser needs to show some form of a busy indicator, and the important questions are: what type of indicator, whether progress can be estimated, and what criteria are used to trigger its display.

Some browsers have already replaced "progress bars" with "indeterminate indicators" that address the pretense of attempting to predict and estimate something that they can't. However, this treatment is inconsistent between different browser vendors, and even same browsers on different platforms — e.g. many mobile browsers use progress bars, whereas their desktop counterparts use indeterminate indicators. We need to fix this.

Also, while we're on the subject, what are the conditions that trigger the browser's busy indicator anyway? Today the indicator is shown only while the page is loading: it is active until the onload event fires, which is supposed to indicate that the page has finished fetching all of the resources and is now "ready". However, in a world optimized for progressive rendering, this is an increasingly less than useful concept: the presence of an outstanding request does not mean the user can't or shouldn't interact with the page; many pages defer fetching and further processing until after onload; many pages trigger fetching and processing based on user input.

Time to onload is bad performance metric and one that developers have been gaming for a while. Making that the success criteria for the busy indicator seems like a decision worth revisiting. For example, instead of relying on what is now an arbitrary initialization milestone, what if it represented the pages ability to accept and process user input?

  • Does the page have visible content and is it ready to accept input (e.g. touch, scroll)? Hide the busy indicator.
  • Is the UI thread busy (see jank) due to long-running JavaScript or other work? Show the busy indicator until this condition is resolved; the busy indicator may be shown at any point in the application lifecycle.

The initial page load is simply a special case of painting the first frame (ideally in <1000ms), at which time the page is unable to process user input. Post first frame, if the UI thread is busy once again, then the browser can and should show the same indicator. Changing the busy indicator to signal interactivity would address our existing issues with penalizing progressive rendering, remove the need to continue gaming onload, and create direct incentives for developers to build and optimize for smooth and jank-free experiences.

Ilya GrigorikIlya Grigorik is a web ecosystem engineer, author of High Performance Browser Networking (O'Reilly), and Principal Engineer at Shopify — follow on Twitter.