Preload (spec1) is a new web standard aimed at improving performance and providing more granular loading control to web developers. It gives developers the ability to define custom loading logic without suffering the performance penalty that script-based resource loaders incur.
Further reading on Smashing: Link
A few weeks ago, I shipped9 preload support in Chrome Canary, and barring unexpected bugs it will hit Chrome stable in mid-April. But what is that preload thing? What does it do? And how can it help you?
Well, is a declarative fetch directive.
In human terms, it’s a way to tell a browser to start fetching a certain resource, because we as authors (or as server administrators, or as smart-server developers) know that the browser is going to need that particular resource pretty soon.
Didn’t We Already Have That? Link
Kinda, but not really. has been supported on the web for a long while, and has decent browser support10. On top of that we’ve also supported
11 in Chrome for some time. So what’s new about preload? How is it different from these other directives? They all tell the browser to fetch things, right?
Well, they do, but there are significant differences between them. Differences that warrant a shiny new directive that tackles many use cases that the old ones never did.
is a directive that tells a browser to fetch a resource that will probably be needed for the next navigation. That mostly means that the resource will be fetched with extremely low priority (since everything the browser knows is needed in the current page is more important than a resource that we guess might be needed in the next one). That means that prefetch’s main use case is speeding up the next navigation rather than the current one.
was originally planned to tackle the current navigation, but it failed to do that in some spectacular ways. Since the web developer had no way to define what the priority of the resource should be, the browser (just Chrome and Chromium-based browsers, really) downloaded it with fairly low priority, which meant that in most cases, the resource request came out at about the same time that it would if subresource wasn’t there at all.
How Can Preload Do Better? Link
Preload is destined for current navigation, just like subresource, but it includes one small yet significant difference. It has an as
attribute, which enables the browser to do a number of things that subresource and prefetch did not enable:
- The browser can set the right resource priority, so that it would be loaded accordingly, and will not delay more important resources, nor tag along behind less important resources.
- The browser can make sure that the request is subject to the right Content-Security-Policy12 directives, and doesn’t go out to the server if it shouldn’t.
- The browser can send the appropriate
Accept
headers based on the resource type. (e.g. advertise support for “image/webp” when fetching images) - The browser knows the resource type so it can later determine if the resource could be reused for future requests that need the same resource.
Preload is also different since it has a functional onload
event (which, at least in Chrome, wasn’t working for the other two rel
values).
On top of that, preload does not block the window’s onload
event, unless the resource is also requested by a resource that blocks that event.
Combining all these characteristics together enables a bunch of new capabilities that were not possible until now.
Let’s go over them, shall we?
Loading Of Late-Discovered Resources Link
The basic way you could use preload is to load late-discovered resources early. While most markup-based resources are discovered fairly early by the browser’s preloader13, not all resources are markup-based. Some of the resources are hidden in CSS and in JavaScript, and the browser cannot know that it is going to need them until it is already fairly late. So in many cases, these resources end up delaying the first render, the rendering of text, or loading of critical parts of the page.
Now you have the means to tell the browser, “Hey, browser! Here’s a resource you’re going to need later on, so start loading it now.”
Doing so would look something like:
The as
attribute tells the browser what it will be downloading. Possible as
values include:
"script"
,"style"
,"image"
,"media"
,- and
"document"
.
(See the fetch spec14 for the full list.)
Omitting the as
attribute, or having an invalid value is equivalent to an XHR request, where the browser doesn’t know what it is fetching, and fetches it with a fairly low priority.
Early Loading Of Fonts Link
One popular incarnation of the “late-discovered critical resources” pattern is web fonts. On the one hand, in most cases they are critical for rendering text on the page (unless you’re using the shiny font-display CSS values15). On the other hand, they are buried deep in CSS, and even if the browser’s preloader parsed CSS, it cannot be sure they’d be needed until it also knows that the selectors that require them actually apply to some of the DOM’s nodes. While in theory, browsers could figure that out, none of them do, and if they would it could result in spurious downloads if the font rules get overridden further down the line, once more CSS rules come in.
In short, it’s complicated.
But, you could get away from all that complexity by including preload directives for fonts you know are going to be needed. Something like:
One point worth going over: You have to add a crossorigin
attribute16 when fetching fonts, as they are fetched using anonymous mode CORS17. Yes, even if your fonts are on the same origin as the page. Sorry.
Also, the type
attribute is there to make sure that this resource will only get preloaded on browsers that support that file type. Right now, only Chrome supports preload, and it does support WOFF2 as well, but more browsers may support preload in the future, and we cannot assume they’d also support WOFF2. The same is true for any resource type you’re preloading and which browser support isn’t ubiquitous.
Dynamic Loading Without Execution Link
Another interesting scenario that suddenly becomes possible is one where you want to download a resource because you know you’d need it, but you don’t yet want to execute it. For example, think of a scenario where you want to execute a script at a particular point in the page’s life, without having control over the script (so without the ability to add a runNow()
function to it).
Today, you are very limited in the ways you can do that. If you only inject the script at the point you want it to run, the browser will have to then download the script before it can be executed, which can take a while. You could download the script using XHR beforehand, but the browser will refuse to reuse it, since the resource wasn’t downloaded with the same type as the one that is now trying to use the resource.
So what can you do?
Before preload, not much. (In some cases you can eval()
the contents of the script, but that’s not always feasible nor without side effects.) But with preload you can!
var preload = document.createElement("link");
link.href = "myscript.js";
link.rel = "preload";
link.as = "script";
document.head.appendChild(link);
You can run that earlier on in the page load process, way before the point you want the script to execute (but once you’re fairly confident that the script loading will not interfere with other, more critical resources that need loading). Then when you want it to run, you simply inject a script
tag and you’re good.
var script = document.createElement("script");
script.src = "myscript.js";
document.body.appendChild(script);
Another cool hack is to use the onload
handler in order to create some sort of a markup-based async loader. Scott Jehl18 was the first to experiment19 with that, as part of his loadCSS library. In short, you can do something like:
and get async loaded styles in markup! Scott also has a nice demo20 page for that feature.
The same can also work for async scripts.
We already have