﻿---
title: Migrating from instant.page to the Speculation Rules API
date: 2026-06-05
excerpt: Migrating from instant.page to the browser-native Speculation Rules API, with notes on prefetch, prerender, side effects, and Swup/PJAX conflicts.
tags:
  - Web
  - Performance
  - Swup
  - Hexo
lang: en
i18n:
  cn: /speculation-rules-api
  translation: 2
updated: 2026-06-26 19:07:59
---

<script type="module" src="/js/components/sidenote.js"></script>

This site previously used [instant.page](https://instant.page/) to optimize internal navigation. Its working model is easy to understand: when the user hovers over a link, the script inserts `{html} <link rel="prefetch" as="document">` and downloads the next page's HTML in advance. When the user actually clicks, the browser waits for one fewer network request, so the page feels faster.

That approach is lightweight and reliable. But it is still a third-party script, and what it does essentially stops at `prefetch`: it can fetch the target document ahead of time, but it cannot start running the page ahead of time.

Chromium-based browsers now offer a more native option: **Speculation Rules API**. It lets us use a piece of JSON to tell the browser which pages are likely to be visited and how far it should prepare them in advance.

<side-note>Compatibility still needs attention: as of 2026-06, stable support is mainly on Chromium; Firefox does not support it yet; Safari is still at the partial-support stage behind a preference flag. So it can only be used as progressive enhancement, not as a prerequisite for page usability.</side-note>

## Scope

Speculation Rules API is for **document navigation**, meaning navigation from one HTML document to another HTML document. It is especially suitable for MPAs, static sites, content sites, and flows such as product listings to product detail pages.

It is not a general-purpose resource preloading tool. The MDN docs explicitly point out that Speculation Rules API does not prefetch a page's subresources. If you want to load a JS chunk, font, or image ahead of time, that is still a matter for `{html} <link rel="prefetch">`, `preload`, or the framework's own resource scheduling.

It is not a mandatory command either. The browser treats the rules as hints, then decides whether to execute them based on network conditions, memory, battery, user settings, and its own heuristics. Browsers that do not support it ignore `{html} <script type="speculationrules">`, so it is well suited as progressive enhancement.

## prefetch and prerender

The two most common actions in Speculation Rules are `prefetch` and `prerender`.

| Action      | What the browser does                                                                 | Cost | Suitable for                                      |
| ----------- | -------------------------------------------------------------------------------------- | ---- | ------------------------------------------------- |
| `prefetch`  | Requests the target document in advance; does not load subresources or run page scripts | Low  | A safer replacement for instant.page              |
| `prerender` | Loads and renders the target page in the background, including subresources and scripts | High | Targets that are very likely to be visited, with controllable side effects |

The table shows that `prerender` is a more aggressive optimization: the browser prepares a hidden page in the background, and the target page's DOM, styles, layout, and scripts may already have run. When the user clicks, the browser can directly activate the background page into the foreground. If the background page is not fully ready yet, it can continue loading with the work already completed.

Public case studies show its upper bound. web.dev has a few relevant numbers: after Ray-Ban used prerender, mobile LCP dropped from 4.69 seconds to 2.66 seconds; Monrif improved desktop LCP by 17.9% and user engagement by 8.9%.[^web-dev-case-studies] These numbers do not mean every blog can reproduce the same gain, but they are enough to show that the API can provide real value in the right scenario.

## Example Rules for a Blog

If the goal is just to replace instant.page, `prefetch` is enough. For this site, internal links in a static blog have relatively simple state, so some ordinary article navigations can be handed to `prerender`.

A restrained starting point can look like this:

```html
<script type="speculationrules">
  {
    "prerender": [
      {
        "where": {
          "and": [
            { "href_matches": "/*" },
            { "not": { "selector_matches": "a[href^='#']" } },
            { "not": { "selector_matches": "a[href*='?']" } },
            { "not": { "selector_matches": "a[target='_blank']" } },
            { "not": { "selector_matches": "a[download]" } },
            { "not": { "selector_matches": "a[rel~='nofollow']" } },
            { "not": { "selector_matches": "a[data-no-prerender]" } }
          ]
        },
        "eagerness": "moderate"
      }
    ]
  }
</script>
```

If the site is more complex, change `"prerender"` above to `"prefetch"` first, replace instant.page with the browser's native capability, and only then consider whether to use `prerender`. That is the more conservative migration order.

## Choosing eagerness

`eagerness` controls when the browser starts predictive loading. It is not "the more aggressive, the better"; it is about finding a point between hit rate and wasted resources.

| Value          | Rough trigger timing                                  | My interpretation                                     |
| -------------- | ----------------------------------------------------- | ----------------------------------------------------- |
| `immediate`    | Starts as soon as possible after the rule is observed | Only suitable for a very small number of almost-certain entries |
| `eager`        | Earlier than `moderate`                               | Good for a few high-probability links; not for site-wide use |
| `moderate`     | On desktop, usually about 200ms after hover, or earlier on pointerdown | A balanced starting point for ordinary content sites  |
| `conservative` | Close to the actual mouse press or touch              | Saves the most resources, with the most limited gain  |

Mobile has no hover, so Chrome uses touch- and viewport-related heuristics to infer intent. Do not mechanically understand `moderate` as "mouse hover for 200ms." This means the same rules can trigger differently on different devices, and the browser will suppress some predictive loading depending on the current environment.

Chrome also has hard limits to prevent sites from overusing user device resources. The current documentation lists these limits:

| `eagerness`                           | Prefetch | Prerender |
| ------------------------------------- | -------- | --------- |
| `immediate`                           | 50       | 10        |
| `eager` / `moderate` / `conservative` | 2, FIFO  | 2, FIFO   |

In other words, for interaction-driven rules such as `moderate`, the browser usually keeps only two candidates at the same time. When a new candidate comes in, an old one will be canceled. Add Save-Data, low-power mode, memory pressure, users disabling "preload pages," and extensions that actively block preloading, and the final decision still belongs to the browser.

## Side Effects This Site Needs to Avoid

The risks of `prerender` depend on the type of application. For a personal blog like this one, there is no login, logout, shopping cart, payment, inventory, or similar state, so there is no need to import every pitfall from business systems.

<side-note>If this were an e-commerce site, admin system, logged-in app, or transaction flow, the risk surface would be much larger: logout URLs, add-to-cart, inventory state, personalized recommendations, order APIs, and so on. This article has a good summary of those risks: <a href="https://erpanomer.nurverse.com/blog/speculation-rules-api-zero-ms-page-navigation#0ms">Speculation Rules API zero-ms page navigation</a>.</side-note>

The first thing this site needs to handle is **analytics bias**. `prerender` can run target-page scripts in the background, so pageview counts may fire before the user truly enters the page. If the user never clicks the link, analytics will be higher than actual visits.

```js
const whenActivated = new Promise((resolve) => {
  if (document.prerendering) {
    // The current page is still prerendering in the background.
    // Continue only after it is actually opened by the user.
    document.addEventListener("prerenderingchange", resolve, { once: true });
  } else {
    // A page entered through normal navigation is already visible,
    // so later initialization can run directly.
    resolve();
  }
});

whenActivated.then(() => {
  // These should run only after the page is truly displayed,
  // to avoid polluting analytics during prerendering.
  initAnalytics();
  initComments();
});
```

The second issue is **extra request pressure**. If `immediate` is used on a large number of links, server request volume will increase accordingly, especially when some of those links include expensive computation or resource requests. The bill follows 🧾

That is also why this site uses `moderate`: wait until the user shows intent before prerendering, which wastes fewer resources.

## Conflict Between prerender and Swup/PJAX

The prerequisite for prerender is document navigation, meaning the browser switches from one HTML document to another HTML document.

Swup and PJAX intercept link clicks, fetch HTML themselves, and update the page content. Throughout this process, the browser still sees only the same document; no real page navigation has happened. Since that is the case, a prepared prerendered page has no way to be activated.

Therefore, this site eventually abandoned Swup, returned to browser-native navigation, and used the cross-document View Transitions API for a small optimization. The current effect is simple: during page switches, the navigation bar no longer flickers with the full page reload.

But all of that was generated by Codex. I do not know the implementation principle at all. After I understand it, I will write a separate analysis.

## Verification Method

The verification method is difficult to explain in text. It takes effort and gives little back. I am tired by this point, so if you are interested, watch the video yourself 👇

<iframe src="https://www.youtube.com/embed/LEF4UaM5m4U" allowfullscreen style="width: 100%; aspect-ratio: 16 / 9; height: auto; border: 0;" ></iframe>

## Conclusion

If the goal is only to replace instant.page, start with Speculation Rules `prefetch`. If you want to use `prerender`, consider the side effects above and its compatibility with SPA navigation approaches such as Swup/PJAX.

[^web-dev-case-studies]: Data source: web.dev's Ray-Ban and Monrif case studies, respectively [Ray-Ban case study](https://web.dev/case-studies/rayban-speculation-rules) and [Monrif case study](https://web.dev/case-studies/monrif-cwv); accessed on 2026-06-04.
