By default, webpages render like this:
Stuff pops in as it’s loaded, parsed, and styled. This is called incremental rendering.
Incremental rendering is cool and good. It is especially cool/good for people on slow connections. They get to see pieces of the page ASAP; incremental rendering shows them something while the page is in flight, instead of nothing.
For the 99th percentile of page loads in a recent batch of RUM Archive data, incremental rendering gave folks something after 10 seconds. Without it, they would have spent 40 seconds looking at nothing.1
While incremental rendering is especially helpful for people in slower contexts, we don’t have to look to the 99th percentile to see tangible benefits. For the median page load, incremental rendering halved the time people waited to see something, cutting it from just over two seconds to just over one.
So that’s incremental rendering. I like it a lot.
Cross-document View Transitions render like this:
Upon clicking a link, we leave one fully-rendered page, and usher in a new fully-rendered page, in an animated/orchestrated manner.
I ❤️ View Transitions. Being able to:
- establish object permanence across navigations, and
- direct attention with motion in order to highlight important arriving/departing elements
…is going to give designers the ability to make websites easier to navigate and understand. We’re going to be able to establish freaking spatial hierarchies across pages, on regular websites! Death to jarring, abrupt transitions!
Death to single-page-apps!
And, uh, death to — incremental rendering? (oh no).
Consider a View Transition to a partially-rendered page.
It might look fine, sometimes. For instance, if an
<img src> hasn’t loaded, but that
<img> element is otherwise all parsed and styled and includes
width attributes, its dimensions will be reserved on the layout. We’ll get a transition to a solid
background-color placeholder or something, which is not the end of the world. A not-loaded-yet font feels similarly not-horrible: we might View Transition in some invisible FOIT text, which, worst-case, triggers a reflow after it loads, jostling surrounding elements some time later. We lose the transition effect, but it’s kinda-sorta falling back to the experience you would have had, without the View Transition.
But. If we initiate a View Transition before things that are producing DOM or layout are loaded and parsed – CSS, HTML, and JS – that could be a disaster. Let’s say a bit of DOM hasn’t been loaded/parsed/styled/rendered yet when a View Transition starts, and then sometime later, when that piece of DOM is ready to render, it ends up having the same
view-transition-name as something on the outgoing page. This means that the author intended for the old element to transition smoothly into the new one. Instead, the old one exits the page, and then the new one pops in, sometime later, not part of the transition process at all.
This is bad. At best it’ll look glitchy. At worst it does the exact opposite of guiding the eye to the most important elements on the page, and establishing object permanence. Instead of showing users that this thing is the same as that thing, we’ve told them, perhaps with extra motion/panache, that even though these the two things might seem the same, they are in fact distinct. It’s like shaking your head “no” while telling someone “yes.” Or perhaps it is more like walking out of the room and then teleporting back into place before you even answer their question. It is disorienting, surprising, and unsettling. It is much worse than having no transition at all.
This is a fundamental problem with View Transitions. And because browsers can and regularly do render partial pages – even on fast machines with fast connections loading fast pages – it’s going to affect everyone.
How to solve it?
The folks prototyping cross-document View Transitions in Chrome are solving this fundamental problem by building and extending features which delay the first paint until the page is ready to transition.
- Already spec’d and implemented:
blocking=renderon scripts and stylesheets
- A proposed extension:
Two important points about these:
- In order to use View Transitions, you are going to have to use these features (no matter how fast your site is).
- You can use these features to disable incremental rendering, even when you’re not using View Transitions.
They are not tied to View Transitions because they are also supposed to solve the other (IMO all bad) use-cases people have for disabling incremental rendering:
If you’re at the 25th percentile in that chart at the top of the post – experiencing a First Contentful Paint at ~600ms, with a total Page Load Time of ~1100ms – trading an extra half-second of time-spent-staring-at-nothing in exchange for a less hurk-jerk-y experience might seem like an improvement.
But if you’re in the 99th percentile, you would never choose to stare at nothing for forty seconds when something — please, anything! — could have appeared after ten.
I worry that giving developers tools to explicitly block render – with or without View Transitions – is going to make experiencing the web on slow connections and cheap devices much worse. Because while the web’s authors generally experience the web at the left side of our chart, and make decisions based on those experiences, significant portions of the web’s users live their lives over on the right.
But not too slowly
The render-blocking specs have made room for implementation-defined timeouts for render-blocking features (ala
font-display: block), and there is currently an open discussion on how/whether to bail out of View Transitions when things are taking too long. I think this is vital. We need tuned timeouts that ensure that the long tail of slow devices/connections don’t wait for View Transitions if they would excessively delay first paint.
It’s probably good that this timeout is left to implementers to define but it’s not great that Chrome currently sets it to network timeout which I think is, like, 240 seconds!? I fully expect this all to get better as View Transitions moves towards shipping, though.
Paint just slowly enough?
The specs also try to give authors tools to tell the browser a bare-minimum amount of HTML, CSS, and JS that’s needed to paint the initial viewport, but that’s incredibly hard to do well. I fully expect everybody who wants to use View Transitions – especially toolmakers and template/component authors who don’t know much about the content they’re working with or the context they’re working in — will simply slap
blocking=render onto everything.
I have low expectations, but sincere hope, that we can do better. Maybe instead of significantly delaying the first paint for View Transitions, quick render-blocking timeouts could serve as a stick which, paired with the carrot of View Transitions, might motivate authors to push their pages to paint faster.
Besides quick timeouts, what else could View Transitions and
blocking=render do, here? What other platform-level changes could make faster and more-complete first paints hard to screw up, rather than hard to do at all – especially on slower connections and devices? I don’t know but I want to spend some time thinking about it.
Low expectations. High hopes!
Thanks to Mat Marquis, Stuart Langridge, and Jason Grigsby for reviewing an early draft!
Important caveat! For this analysis, I used two metrics: Page Load Time and First Contentful Paint. (I also assume they are perfectly correlated, but let’s leave that aside; bigger fish to fry). First Contentful Paint is the exact metric we need; Page Load Time isn’t. The proposed features would delay first paint until the document and many scripts and stylesheets have been loaded. Page Load Time, on the other hand, measures how long it took for everything to load, notably including images (which the proposed features would not wait for). So this chart likely overstates the case. I would love to get a more accurate picture of the potential impact of the proposals; alas this is the best I know how to do at the moment. ↩