← All posts

Preloading before the click: the Speculation Rules API on a static site

On a static, multi-page site with no JavaScript framework, every click on a link triggers a real page load. That's what makes these sites light and reliable. But it also means there's a small dead time on every navigation: the request goes out, the server responds, the page renders. A recent browser API, the Speculation Rules API, lets that dead time start before the click happens. Here's what it does, and how I use it — carefully.

The problem it solves

Sites that feel instantly fast are, most of the time, JavaScript applications that handle navigation themselves (client-side routing): they just swap the page content without a full reload. It works, but it has a cost — more JavaScript to download and run, which usually means less reliability and a slower time-to-interactive on the first load.

On the sites I build, I prefer the opposite: little JavaScript, real pages, a real reload on every navigation. The Speculation Rules API lets me keep that simple architecture while cutting the perceived dead time between pages, without writing a single line of routing code.

What the API actually does

The idea: you declare, in a JSON block, which pages the browser is allowed to fetch ahead of time — before the visitor even clicks. There are two levels. prefetch just downloads the server response in the background. prerender goes further: it loads and renders the page in a hidden tab, ready to appear instantly on click.

It's declared with a simple <script type="speculationrules"> block:

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

The eagerness field controls how aggressive the browser is: conservative waits for an actual click to start (mousedown), moderate triggers as soon as a link is hovered or scrolled into view, immediate loads right away. The more aggressive the setting, the faster the next navigation feels — and the more pages get loaded that a visitor may never actually visit.

What I use, and what I avoid

I always start at moderate: a good balance that mostly preloads pages a visitor is genuinely looking at (hover, scroll), without blindly preloading everything. And I explicitly exclude certain links from prerender: a contact page if it contains a form, any logout link, any external link. The reason is simple — a prerender loads and runs the page in the background, JavaScript included. If that page triggers a side effect (firing an analytics event, invalidating a session), it would happen before the visitor has even clicked.

It's also one more reason I don't add third-party trackers to the sites I ship: fewer scripts with side effects means fewer risks like this one with speculative preloading.

One more caution: it doesn't work everywhere yet

This API isn't a universally supported standard. Today it works in Chromium-based browsers (Chrome, Edge); Firefox and Safari don't implement it yet, and it isn't considered "Baseline" (MDN's marker for a feature that reliably works everywhere). That's not a real problem — it's exactly the kind of feature to treat as progressive enhancement. A browser that doesn't understand it simply ignores the <script> block and loads the page normally, as before. Nothing breaks, no one is penalized — some visitors just get a faster navigation than others.

What it changes for a visitor

In practice: on a site that uses prerender carefully, clicking an internal link can feel like the next page was already there. No client-side routing, no extra framework, just a small JSON block in the <head>. That's the kind of improvement I like: it adds no complexity for the visitor, asks for no compromise on the site's simplicity, and only ever moves in one direction — toward faster, never toward less reliable.

Got a site that drags on navigation, or a project worth rebuilding on lighter foundations? Let's talk.

Got a project in mind? Let’s talk.

Get in touch