Speculation Rules for Faster Page Loads

Show QR code Hide QR code
QR code linking to https://navendu.me/tils/17-8-25-speculation-rules-for-faster-page-loads/

The Speculation Rules API allows you to improve page navigation on your websites by prefetching and prerendering web pages proactively with just a few lines of code.

In practice, this means that when a user hovers over a link (to click it), the next web page is already loaded and rendered, providing a faster, SPA-like loading experience.

I added it to my website today. You can see it in action by opening the network tab and hovering over a link in this website without clicking it.

This website uses Hugo, so I added it to my head.html partial template:

<!-- Speculation Rules for instant navigation -->
{{- /* Only enable this in production */}}
{{- if hugo.IsProduction | or (eq site.Params.env "production") }}
<script type="speculationrules">
  {
    // Prefetch and prerender all internal pages but wait 200ms (moderate eagerness) after hover
    "prerender": [{ "where": { "href_matches": "/*" }, "eagerness": "moderate" }],
    "prefetch": [{ "where": { "href_matches": "/*" }, "eagerness": "moderate" }]
  }
</script>
<script>
  // Fallback for browsers that don't support the Speculation Rules API
  if (!HTMLScriptElement.supports || !HTMLScriptElement.supports('speculationrules')) {
    // Track preloaded URLs to avoid duplicate requests
    const preloadedUrls = {};

    function pointerenterHandler() {
      if (!this.href) return; // Skip if no href
      if (!this.href.startsWith(location.origin)) return; // Skip external links
      if (preloadedUrls[this.href]) return; // Skip duplicates
      preloadedUrls[this.href] = true; // Mark as preloaded

      const prefetcher = document.createElement('link');
      const supportsPrefetch = prefetcher.relList && prefetcher.relList.supports && prefetcher.relList.supports('prefetch'); // Check if browser supports prefetch
      prefetcher.as = supportsPrefetch ? 'document' : 'fetch'; // Set as document or fetch (older browsers)
      prefetcher.rel = supportsPrefetch ? 'prefetch' : 'preload'; // Set as prefetch or preload (older browsers)
      prefetcher.href = this.href;
      document.head.appendChild(prefetcher); // Add to head
    }

    document.addEventListener('DOMContentLoaded', function () {
      const conn = navigator.connection || navigator.mozConnection || navigator.webkitConnection;
      const saveData = conn && conn.saveData;
      const isVerySlow = conn && /(^|-)2g($|-)/.test(String(conn.effectiveType || ''));
      if (saveData || isVerySlow) return; // Skip for data saver and slow connections
      document.querySelectorAll('a[href^="/"]').forEach(function (item) {
        item.addEventListener('pointerenter', pointerenterHandler, { passive: true }); // Add event listener when pointer enters link area
      });
    });
  }
</script>
{{- end }}

DocuSeal has an excellent blog post that covers this API. The code I shared above just tweaks it a little bit.

Webmentions • Last updated at 10:52 AM, 6th April 2026

Have you written a response to this? Send me a webmention by entering the URL.