Motorcycle on a highway

Bringing instant page-loads to the browser through speculative prerendering

Bringing instant page-loads to the browser through speculative prerendering

Learn more about speculative prerendering in the browser and how to participate in Chrome origin trial.

Updated

Resource hints, such as preconnect, preload, prefetch, and prerender, help the browser figure out which resources will the user need in the near future.

preconnect and preload are declarative hints—the browser must act on them, so use them when you are sure that a resource will be required during the next load.

prefetch and prerender are speculative hints—use these to recommend that the browser should fetch a certain resource because there's a high chance it might be required.

This article focuses on speculative prefetching and prerendering. Learn more about how they're used, the drawbacks of the current implementations, and popular external libraries that implement sophisticated speculation. Enhancements that bring same-origin speculative prerendering to the browser are under development and you can learn more about their design and participate in Chrome origin trial.

prefetch and prerender: Current implementation #

Users looking at a list of links relevant to their interest (for example, a list of products or articles matching a search keyword or the user's preferences) will likely click on the links at the top. If they navigate back to the list page, they might click the following link in the list. prefetch and prerender rely on this knowledge of the user's behavior. Developers speculate which page (B) is likely to be requested after a specific page (A).

prefetch hint #

When developers include a prefetch hint on page A to tell the browser that it can fetch either page B or specific resources on page B in advance, the browser can fetch those resources while it is idle without affecting the processing of page A.

The syntax for using prefetch in the origin page (page A of our example) is as follows:

<link rel="prefetch" href="/results/" as="document">

The as attribute here is optional but helps the browser set the correct headers required to determine if the resource is already in the cache.

The available support for prefetch and implementation options have matured slightly over the last couple of years.

Browser support: chrome 8, Supported 8 firefox 2, Supported 2 edge 12, Supported 12 safari, Not supported ×

Source

prerender hint #

Including a prerender hint tells the browser to render page B in advance. Prerendering a page enables an instant navigation experience when the user actually clicks on the link for page B.

The syntax for using prerender in the origin page is as follows:

<link rel="prerender" href="/next-page/">

prerender behavior, however, is still not clearly defined or universally implemented.

Browser support: chrome 13, Supported 13 firefox, Not supported × edge 79, Supported 79 safari, Not supported ×

Source

Prefetch implementation #

There are a few important points to note about prefetch behavior (especially in Chrome):

  • You can prefetch either the next page entirely or same-origin subresources such as stylesheets or scripts that would be required by the next page.
  • The prefetched resources are stored in the HTTP Cache if they are cacheable.
  • Chrome keeps the cached items for 5 minutes.
  • When the resources are requested or required they are retrieved from the cache. However, if they have not finished loading the partially loaded resource is picked up by Chrome to continue loading it.
  • Prefetching of resources consumes extra bytes and is not recommended if a user has data-saver mode enabled. The connection type and other details about users' data preferences can be determined via the Network Information API.

Prerender implementation using no-state prefetch #

The original implementation for the prerender hint in Chromium was using a lot of memory so it was deprecated in favour of a no-state prefetch which fetches resources in advance but it does not execute JavaScript or render any part of the page in advance. Originally prerender consumed around 100 MiB of memory and could potentially disrupt UI when certain components like media were prerendered.

The current implementation using a no-state prefetch consumes significantly lower memory of around 45 MiB, while still reducing page load times. The fetch is triggered by a link element with a prerender resource hint. The no-state prefetch is carried out in a new dedicated renderer that is isolated from the visible tabs and cannot disrupt UI. The no-state prefetch process caches the resources required but does not render them. It also does not modify the state of the browser except when updating the DNS cache and the cookie store. The new renderer is killed after all subresources are loaded. A new renderer is created to render the page when the user requests it.

With a no-state prefetch implementation for prerender, the goal of instant page loads still eludes us. Before exploring how we can achieve instant page loads, let's take a look at some out-of-the-box options that implement speculation logic for prefetch which may also be relevant to the prerender implementation.

Smart speculation with third-party libraries #

Developers have insight into how people use their site and use that knowledge to decide what should be prefetched or prerendered. These usage trends tend to evolve over time, so developers need to update their resource hints based on the latest analytics data available for the site.

There are also libraries such as quicklink and guess.js, that use heuristics to determine which resources should be prefetched at runtime. With these, developers don't need to guess what should be prefetched. The libraries take the decision based on available data.

Quicklink uses Intersection Observer API to determine which links are in the viewport and prefetches them when the browser is idle if the user is not on a slow connection. It uses the Network Information API to determine the type of connection and if the data-saver mode is enabled. Quicklink is a lightweight library that you can use with both multiple-page apps and single-page apps to speed up navigations.

Guess.js #

Guess.js implements predictive prefetching based on a report generated by Google Analytics or a similar analytics provider. These analytics-based predictions are used to prefetch resources that the user is likely to need.

Let us now see how similar logic can be built inside the browser and what additional tooling would be required to support that.

Implementing a revamped prerendering solution #

The current prerendering implementations do not address several constraints. Following are some of the limitations that were discussed and proposed solutions that could improve prerendering.

  • Triggers: Currently <link rel="prerender">is the only trigger available to enable prerendering in the browser. For every link on the page which is eligible for prerendering, an additional resource hint needs to be included in the document during development.

    Since prerender requirements may change, the proposed solution should allow users to specify blanket triggers for links that match certain criteria. This has been made possible through the speculation rules API discussed in detail later.

  • Cross-origin prerender: When the target link of a prerender is on the same origin, there are not many additional checks required. However, when it is pointing to a different origin, there may be privacy concerns that the browser should address. For example, user credentials should be omitted when prerendering a cross-origin page because the response should not be personalized before an actual navigation occurs. However, user-related information may be provided when the user navigates to the page. To support this two stages approach, there should be a way for the target pages to opt-in to being prerendered by a cross-origin page.

  • Prerendering browsing context: Currently, there are different types of top-level browsing contexts available. For example, a tab in a window or an iframe on a page uses a different browsing context. A similar top-level prerendering browsing context should be created for prerendered content.

    The prerendering browsing context should be similar to an invisible tab and should impose additional restrictions. All disruptive APIs like playing media or permission prompts that may disrupt UI should be disabled in this browsing context.

    The prerendering browsing context may also be activated. Activation should take place when the user navigates to a prerendered page. When activated, the prerendered page would switch to a new top-level browsing context.

  • Portals: Portal is a new proposed HTML element that enables seamless and instant navigations between pages. It would allow you to display the prerendered content as shown.

        <portal id="myPortal" src="https://example.com/"></portal>

    This element would provide a preview of the prerendered page in a prerendering browsing context. This implies that the page preview will have restricted permissions. ​​Developers may activate the context through code on, say, a click event to expand the portal with animation to a full-page view in the embedding window.

In-browser speculation rules for prefetch and prerender #

One of the most important pieces of prerendering discussed in the previous section is prerendering triggers available through the proposed Speculation Rules API. The Speculation Rules API can be used by developers to indicate blanket permissions to the browser to speculate and prefetch or prerender pages that match the specified criteria.

The rules help the browser identify an initial set of pages that the website thinks would interest the user. The browser can then apply additional heuristics based on device or network characteristics, page structure, viewport, the location of the cursor, past activity on the page, and so on to decide which pages to prerender or prefetch. Thus, speculation logic like implemented by QuickLink or Guess.js may be implemented by the browser itself.

Speculation rules may be specified as a JSON object within an inline script tag or an external resource. For example, speculation rules for prerender may be defined as follows:

<script type="speculationrules">
{
"prerender": [
{
"source": "list",
"urls": ["/page/2"],
"score": 0.5
},
{
"source": "document",
"if_href_matches": ["https://*.wikipedia.org/**"],
"if_not_selector_matches": [".restricted-section *"],
"score": 0.1
}
]
}
</script>

Here, two types of rules have been defined for the prerender resource hint.

  • List rules apply to the list of given urls. The score value is used to indicate how likely the user is to navigate to one of these URLs next. The score value can be between 0.0 and 1.0 with a default of 0.5.

  • Document rules apply to a document implying that all link elements within a page are open to speculation by the browser. The subset of link elements may be chosen by including the if_href_matches or if_not_href_matches and if_selector_matches or if_not_selector_matches filters.

Here href_matches is used to match the link URLs, while selector_matches is used to match the CSS selectors.

Same-origin prerendering trial #

An initial implementation for prerendering that covers some of the previously discussed features is available as a Chrome origin trial which will run from Chrome 94 to 98. Following are the key features included in this trial.

  • Triggers: Certain features from the Speculation Rules API may be used to specify triggers to prerender same-origin URLs. Only the "list rules" format is supported at present. Also, only one prerender is allowed per page for pages with the same origin.
<script type="speculationrules">
{
"prerender": [
{"source": "list", "urls": ["https://a.test/foo"]}
]
}
</script>

If multiple rules are specified, Chrome always prerenders based on the first rule. The score property is not used. Rules may be added, but the removal of rule sets is ignored.

  • Restricted APIs: APIs that can disrupt the UI such as Geolocation, Web Serial, Notifications, Web MIDI, and Idle Detection, are deferred until the prerendered page is activated.

  • Session access: The prerendered page clones the session object of the tab-level session when it is created. Upon activation, it discards this clone and again takes the latest session object from the tab.

  • Resources: The prerendered page can load all resources like a normal page, except cross-origin iframes which are loaded only upon activation. Cookies and Storage APIs also function as they would on a normal page.

  • Trial usage: The origin trial token must be included on both the page where speculation rules are specified and the page which is the intended target of a prerender.

Using the trial #

Once you start using the trial, you can check if pages are being prerendered and study the performance impact by using existing Chrome tools.

Was the page prerendered? #

The chrome://process-internals page can tell if a prerendered page exists.

Process internals page.

Both the page that initiates the prerender through speculation rules and the prerendered page will be under the same webcontents block but can be differentiated by the prerender keyword.

Webcontents block showing the initiator page and the prerendered page.

Was the prerendered page activated? #

After prerendering, the next step is activation. To check if it was indeed the prerendered page that was activated upon navigation and no new page load occurred, you can open Dev Tools console after the navigation occurs. Execute the following script and check the value of activationStart in the console.

let activationStart = performance.getEntriesByType('navigation')[0].activationStart;
console.log(activationStart);

A non-zero value for activationStart would imply the prerendered page was activated.

Was it instantaneous? #

The activationStart value is a timestamp that can be compared to the values of First Paint and First Contentful Paint to determine the user-centric performance metrics for the prerendered page.

// When the activation navigation started.
let activationStart = performance.getEntriesByType('navigation')[0].activationStart;

// When First Paint occurred:
let firstPaint = performance.getEntriesByName('first-paint')[0].startTime;

// When First Contentful Paint occurred:
let firstContentfulPaint = performance.getEntriesByName('first-contentful-paint')[0].startTime;

console.log('time to first paint: ' + (firstPaint - activationStart));
console.log('time to first-contentful-paint: ' + (firstContentfulPaint - activationStart));

Comparing the values of firstPaint and firstContentfulPaint to those before using the trial, can help you measure the performance impact. It is also recommended that you use real user monitoring (RUM) methods to measure the performance of the origin trial.

Demo #

To check out a simple demo for the prerendering trial, enable the enable-prerender2 flag in Chrome. You will need to enable this for the demo to work.

Chrome flags page.

The demo provides options to prerender three different page types using <link rel=prerender> and the Speculation Rules API. You can click on each of the available options to check if a prerender takes place.

Prerendering demo on Glitch.

You can also compare the transitions for each case by clicking on the link for the page.

Timer.html with speculation rules
Timer.html without speculation rules

Feedback welcome #

The aim of this origin trial is to provide the capability of near-instantaneous page loads through the browser without relying on any external library. At the same time, it tries not to disrupt the user experience and provide a good start to a more sophisticated prerendering journey. Sign-up for the prerendering origin trial and see how well it works for your specific use cases. If you have any feedback on the trial, submit an issue to the Github repo.

There is also an ongoing trial for using Speculation Rules for Prefetch. Sign-up for this trial if you would like to replace your existing prefetch hints with browser supported speculative prefetch.

Last updated: Improve article