0s loading times with the Speculation Rules API


No matter how much you optimize, there’s a hard limit: pages can only start loading after the user clicks. You’re stuck waiting for DNS lookups, server responses, and rendering - even on a fast site, that’s hundreds of milliseconds.

But what if the browser could predict where the user’s going next and start loading that page before they click? That’s what Chrome’s Speculation Rules API does. You can achieve 0ms load times - the page just appears.


Understanding Prefetch vs Prerender

The Speculation Rules API gives you two strategies:

Prefetching downloads just the HTML document. When the user clicks, the browser has a head start but still needs to fetch CSS, JS, and images.

Prerendering goes all-in - it loads everything in a hidden background tab, executes JavaScript, and renders the full page. When the user clicks, the browser just swaps tabs. Instant. No loading screen.

This is the modern replacement for the old <link rel="prerender"> tags (which were deprecated due to privacy issues). The new API is smarter and gives you way more control.

Why This Matters: Perceived Performance

Just because you get a perfect Lighthouse score - doesn’t always mean that your site feels fast. Even optimized pages need DNS lookup, TCP handshake, TLS negotiation, server response, and rendering. That’s hundreds of milliseconds minimum.

Speculation rules eliminate perceived wait time by working during the user’s decision-making. When someone hovers over a link for 200ms, you can use that time to pre-load or pre-render the destination. By the time they click, the work’s done.

The benefits:


Implementing Speculation Rules

Speculation rules are defined in a <script type="speculationrules"> tag with a JSON configuration. Let’s look at practical examples for different use cases.

Example 1: Conservative Prerender (Safest)

Prerender when a user starts clicking. This version uses exclusions to prevent the browser from accidentally triggering background actions:

<script type="speculationrules">
{
  "prerender": [
    {
      "where": {
        "and": [
          { "href_matches": "/*" },
          { "not": { "href_matches": "/logout" } }
        ]
      },
      "eagerness": "conservative"
    }
  ]
}
</script>

Why the not rules? Some links trigger actions just by being visited (like logging out or adding an item to a cart via a URL). Since prerendering “visits” the page in a hidden tab, we exclude these paths to prevent the browser from accidentally logging the user out or changing their cart state while they are just browsing.

Prerender on 200ms hover—balances performance and resource cost. This is the simplest catch-all for general content pages:

<script type="speculationrules">
{
  "prerender": [
    {
      "where": {
        "href_matches": "/*"
      },
      "eagerness": "moderate"
    }
  ]
}
</script>

Example 3: Prefetch Specific Pages, Prerender on Hover

Eagerly prefetch your most important pages on page load, then prerender any link the user hovers over:

<script type="speculationrules">
{
  "prefetch": [
    {
      "urls": [
        "/products",
        "/pricing",
        "/docs"
      ],
      "eagerness": "eager"
    }
  ],
  "prerender": [
    {
      "where": {
        "href_matches": "/*"
      },
      "eagerness": "moderate"
    }
  ]
}
</script>

Example 4: Privacy-Preserving Cross-Origin Prefetch

Prefetch external links with privacy protections. The anonymous-client-ip-when-cross-origin flag uses Chrome’s private prefetch proxy to hide the user’s IP address:

<script type="speculationrules">
{
  "prefetch": [
    {
      "urls": [
        "https://example.com/article",
        "https://partner-site.com/page"
      ],
      "requires": [
        "anonymous-client-ip-when-cross-origin"
      ],
      "referrer_policy": "no-referrer"
    }
  ]
}
</script>

Example 5: CSS Selector-Based Rules

Prerender only links with specific classes:

<script type="speculationrules">
{
  "prerender": [
    {
      "where": {
        "selector_matches": ".prerender-this"
      },
      "eagerness": "eager"
    }
  ],
  "prefetch": [
    {
      "where": {
        "selector_matches": ".prefetch-this"
      },
      "eagerness": "moderate"
    }
  ]
}
</script>

Example 6: Blog with Article Navigation

Prefetch the next/previous article links immediately, prerender related articles on hover:

<script type="speculationrules">
{
  "prefetch": [
    {
      "where": {
        "selector_matches": ".pagination-link"
      },
      "eagerness": "immediate"
    }
  ],
  "prerender": [
    {
      "where": {
        "and": [
          { "selector_matches": ".related-article" },
          { "href_matches": "/blog/*" }
        ]
      },
      "eagerness": "moderate"
    }
  ]
}
</script>

Example 7: E-commerce Product Pages

Prefetch category pages, prerender product pages, but exclude cart and checkout:

<script type="speculationrules">
{
  "prefetch": [
    {
      "where": {
        "href_matches": "/category/*"
      },
      "eagerness": "eager"
    }
  ],
  "prerender": [
    {
      "where": {
        "and": [
          { "href_matches": "/product/*" },
          { "not": { "href_matches": "/cart" } },
          { "not": { "href_matches": "/checkout" } },
          { "not": { "href_matches": "*?add-to-cart=*" } }
        ]
      },
      "eagerness": "moderate"
    }
  ]
}
</script>

Example 8: Documentation Site with Search Parameters

Prerender doc pages but exclude search results and dynamic filters:

<script type="speculationrules">
{
  "prerender": [
    {
      "where": {
        "and": [
          { "href_matches": "/docs/*" },
          { "not": { "href_matches": "*\\?*" } }
        ]
      },
      "eagerness": "moderate"
    }
  ]
}
</script>

Example 9: Multi-Language Site

Prerender pages in the current language, prefetch language switcher links:

<script type="speculationrules">
{
  "prefetch": [
    {
      "where": {
        "selector_matches": ".language-switcher a"
      },
      "eagerness": "conservative"
    }
  ],
  "prerender": [
    {
      "where": {
        "and": [
          { "href_matches": "/en/*" },
          { "not": { "selector_matches": ".language-switcher a" } }
        ]
      },
      "eagerness": "moderate"
    }
  ]
}
</script>

Example 10: SPA-Style Navigation

Aggressively prerender all internal navigation while excluding external links and auth flows:

<script type="speculationrules">
{
  "prerender": [
    {
      "where": {
        "and": [
          { "href_matches": "/*" },
          { "not": { "href_matches": "/login" } },
          { "not": { "href_matches": "/logout" } },
          { "not": { "href_matches": "/api/*" } },
          { "not": { "href_matches": "https://*" } }
        ]
      },
      "eagerness": "moderate"
    },
    {
      "where": { "selector_matches": ".prefetch-this" },
      "eagerness": "moderate"
    }
  ]
}
</script>

Choosing Your Eagerness Level

The eagerness setting controls when speculation happens:


Browser Support: Can You Use It?

The Speculation Rules API is Chromium-only but works as Progressive Enhancement: unsupported browsers just ignore the script tag.

BrowserSupportNotes
Chrome / EdgeFullSupported since Chrome 109 / Edge 109 (January 2023).
Brave / OperaFullFull support via the Chromium engine.
SafariBehind FlagAvailable in Safari 26.2+ behind a flag (not enabled by default).
FirefoxNoCurrently does not support the Speculation Rules API.

Important Guardrails

Prerendering uses bandwidth and battery, so Chrome includes smart protections to prevent abuse:

Final Thoughts

I’m honestly surprised I hadn’t heard about this sooner. Now that all major Chromium browsers support the API, it’s definitely worth using. It’s easy to get caught up in complex performance optimizations and overlook the simpler wins - like how proper semantic HTML improves SEO, or how vanilla CSS can outperform heavy frameworks. The Speculation Rules API fits right into that category in my book: powerful, straightforward, and built into the platform. I can’t wait to keep experimenting with this and see how far it can go.


Further Reading