<100ms E-commerce: Instant loads with Speculation Rules API
<100ms E-commerce: Instant loads with Speculation Rules API
In e-commerce, we all know that speed = money. I know it, you know it, Amazon knows it, eBay knows it, Shopify knows it, everyone knows it. In this article we’ll see how we can improve the perceived performance of our site’s critical pages, like the Product Details page, the Cart page, the Checkout page. We’re going to use the Speculation Rules API (SRA) to prerender/prefetch them, and also explain how certain frameworks like Next.js offer their own prefetching mechanisms.
Speculation Rules API
The SRA is an experimental feature available only in Chromium browsers that allows websites hint to the browser which pages a user is likely to visit next, so the browser can start either preloading or prerendering them ahead of time. This makes navigating to those pages feel instant.
prerender rules make the browser fully download, render, and load them in an invisible tab. This will load all subresources, run all JavaScript, and even perform data fetches started by JavaScript. Navigating to a prerendered page is instant. It’s literally the browser “switching tabs” to a completely loaded page.
prefetch rules make the browser download the page’s document only. It doesn’t render the page in the background, or execute any JavaScript, or load any subresources. It just skips the main document HTTP request upon navigation, but the page will still need to be rendered, and resources loaded. This still leads to major performance improvements, but it’s significantly lighter than the prerender rules.
These rules are added on the page through a <script type="speculationrules"> element, and are defined with JSON:
<script type="speculationrules">
{
prefetch: [
{
source: "document",
where: {
href_matches: "/products/.*",
},
eagerness: "moderate",
},
{
source: "list",
urls: ["/cart"],
},
],
}
</script>The speculation rules above will prefetch all of the /products/* matching URLs that are present on that page eagerly, and also the /cart page. It doesn’t matter how you add these rules - you can hardcode them at the bottom of the <head>, or you can dynamically generate them with JavaScript at runtime. There’s no “deadline” for adding these rules. The browser will start prefetching/prerendering the moment it sees them in the DOM.
There are options for the eagerness, different types of matchers and selectors, and other gotchas, and we won’t cover them in this article, so make sure to check out the official Speculation rules API doc on MDN.
Speculation rules API fallback for different browsers
Since SRA is only available in modern versions of chromium-based browsers, our Safari and Firefox users will miss out on that love. But there’s still something we can do for them too. We can’t really replicate the SRA functionality, but we can leverage the framework-specific prefetching feature that most modern frameworks provide. Usually it’s in a form of a prop or element attribute that you set to a link that makes the framework prefetch that page either on page load, on hover, or skip prefetching it altogether.
Most of the frameworks automatically prefetch pages either on page load, or as they get scrolled in. For example, both Next.js (docs) and Nuxt 3 (docs) prefetch pages by default. SvelteKit (docs) and Remix (docs) do not by default, but give you options to define prefetching. Visit the docs links of your framework of choice to see how you can configure prefetching.
Having framework-level prefetching enabled is a great fallback because it still improves the navigation performance of non-chromium browsers, but also won’t hurt chromium browsers. In case where a chromium browser loads a page with both speculation rules and framework-specific prefetches, the speculation rules will take precedence. For example, let’s say all /product/* pages are prefetched through speculation rules, but on hover as well. When the user lands on the page, the speculation rules will make the browser prefetch all product pages and cache them, so when the user hovers on one of the products the browser won’t prefetch it again since it’s already in cache.
How SRA improves storefront performance
To put all of this to a test, I developed (ahem, vibe coded) a demo Next.js storefront and instrumented it with Sentry’s Next.js SDK. The SDK automatically measures page loads, but to distinguish the prerender metrics from the prefetch ones, I needed to just add a tag with the value of the optimization mode whenever it changed:
Sentry.setTag('optimization_mode', optimizationMode);After this, all I needed to do is create a custom widget that charts the navigations grouped by the optimization method:
(widget specs - spans dataset, p90 of span.duration visualization, filter: span.description contains /products/:id and span.name contains navigation, grouped by optimization_mode)
And look at that! Quite the difference between prefetching/prerendering and no optimization at all. No optimization also means the framework-level prefetch is disabled. This data is obtained from a Vercel deployment, triggered by an automated script. Even on my painfully simple demo app the improvement is significant. I would anticipate the improvement to be much bigger on a real e-commerce storefront with tons of other features like analytics, widgets, all sorts of scripts.
Now from this chart we can’t really say for sure that prerendering is faster/better than prefetching, or vice versa. But one thing is for sure - any optimization is times better than no optimization at all. I would encourage you to set up Sentry’s Next.js SDK in your application to measure and see the difference in your app. Your values will definitely look different than mine above, and the difference between prerendering and prefetching could be more obvious.
Speculation rules gotchas
The SRA does not represent a free performance boost. Just like anything, it comes with some caveats that you should be mindful of. Here are some of them:
Server load and bills
When prerendering, we’re running full SSR + data calls even if the user never clicks on those pages. To fix this, prefer prefetch over prerender, use prerender sparringly, only for links of highest following probability.
If there are many links in the viewport, there are going to be many prefetches/prerenders. This is a classic thundering herd scenario. To fix this, use conservative eagerness in SRA, and set
prefetch={false}on low-prob links.
Analytics / experiments
Since prerendering also invokes all JavaScript on all of the prerendered pages, that means analytics SDKs will also be invoked, resulting in inflated pageviews / conversions. To fix this, you can fire analytics events on activation - check
document.visibilityState === 'visible'after thevisibilitychangeevent, or if you’re doing analytics on the server-side, set a Speculation Rule Tag and look for that header, or the Sec-Purpose header.If you’re also running A/B tests, you’ll experience results skew as well. To fix this, assign experiments on activation, defer experiment beacons until visible.
Product behavior quirks
Prerendering may use current cookies. Tokens can expire before activation, which will result in an auth mismatch / stale session. To fix this, use short-lived cookies, refresh on activation.
Various side-effects during render, like inventory holds, impressions, “last seen” tracking, etc. To fix this, make SSR/data idempotent + read only, and move any side-effects to activation or click.
This is far from an exhaustive list of prerendering/prefetching gotchas, so make sure you do your due diligence - profile real traffic, monitor server load, verify analytics accuracy, and test under production conditions before you roll it out broadly.
Speculation rules in your application
Now that we know the benefits and caveats, let’s talk about how to use the Speculation Rules API in a balanced and strategic way.
We know that prerendering is heavier than prefetching, but it does more of the loading upfront, which should result in better performance. With that in mind, we should use prerendering sparingly. In an e-commerce storefront scenario, I would use prerendering only on a smaller products section, like a “Featured products” section at the top that lists at most 4-5 products, or on event-based sections like a Black Friday banner that’s too good to pass on. These types of sections usually have big CTAs and draw more clicks to them.
Prefetching on the other hand is lighter, but still an important optimization approach. I would use prefetching on other less important (but still important) product cards, or category pages linked in a navigation bar. I would even configure the prefetch to be on “hover” instead of page load just to be safe. If the Black Friday section is first-class, these would be second-class. Prefetch them, but be mindful of how many pages you’re prefetching (unless you prefetch on hover).
For all the other pages that aren’t that important, you can either disable the framework-imposed automatic prefetching to ease up your server load and bills, or keep hover-only prefetching.
Make sure to use prerendering on the critical flow - the flow that “makes money” - user lands on homepage, visits a product, adds to cart, goes to checkout... Strategically placing prerendering rules on pages on this flow will significantly improve the performance of the critical flow, and result in more conversions, more checkouts, more money spent on your website. That’s what you ultimately want, right? With the Speculation Rules API, you can be really smart about this.
Most importantly, make sure to keep an eye on the performance in production after you deploy the improvements. Set up Sentry in your project, and create a Critical Experience Monitoring Dashboard that gives you an overview of how the critical flow is performing, and if you need to improve a certain part of it:
Bringing it all together
In this article we saw how the Speculation Rules API (and framework-level prefetching) can drastically improve the perceived performance of your e-commerce storefront by doing the loading upfront. We used Sentry to measure the performance boost in a production environment and concluded that prerendering/prefetching pages significantly improves the page load performance compared to no optimization at all.
But all that improvement doesn’t come without gotchas. We saw how SRA / prefetching can increase your server load, skew your analytics, and even prerender pages with stale auth cookies. Nothing to be afraid of though! If you have eyes on your app in production (ahem, use Sentry) you can react fast and fix any issue before it affects a large number of users.



