{
"$type": "site.standard.document",
"bskyPostRef": {
"cid": "bafyreiav4o7d625btjgpx5c2ax4x5oiqswuk2hda355bvueufzaojknsse",
"uri": "at://did:plc:svkyjirwpd7ts4qgnzoqfcc2/app.bsky.feed.post/3moih3a6allmx"
},
"coverImage": {
"$type": "blob",
"ref": {
"$link": "bafkreigtizlpbsvgfyaqhmobzktof3evapibq5emfghrs7ojg2xmai22ai"
},
"mimeType": "image/png",
"size": 15991
},
"description": "It appears the shiny new WordPress version 6.8 comes with Speculation Rules out of the box. This is a really interesting optimisation feature, enabling us to programmatically tell the browser (Chrome) to get pages ready for the user to visit. It's pretty dense and complex, but potentially a great optimisation option. Basically it behaves as...",
"path": "/2025/04/23/its-hard-to-speculatively-load-what-tomorrow-may-bring/",
"publishedAt": "2025-04-23T11:32:03.000Z",
"site": "at://did:plc:svkyjirwpd7ts4qgnzoqfcc2/site.standard.publication/3mhpwfentz6lr",
"tags": [
"Code"
],
"textContent": "It appears the shiny new WordPress version 6.8 comes with Speculation Rules out of the box. This is a really interesting optimisation feature, enabling us to programmatically tell the browser (Chrome) to get pages ready for the user to visit. It’s pretty dense and complex, but potentially a great optimisation option. Basically it behaves as if the page has been opened in an invisible background tab. As the API states: This can therefore also have a direct impact on a site’s Core Web Vitals, with near zero LCP, reduced CLS (since any load CLS happens before the initial view), and improved INP (since the load should be completed before the user interacts). This is a slightly spooky feature that I’ve applied manually to this site via data attributes, mostly on the menu as this is where I believe we want most of the goodness for our spicy preloads. I’ve taken inspiration from the excellent CSS Wizardry who walks us through the Speculation Rules API where we look at the following approaches:prefetch pays the next page’s TTFB costs up-front and ahead of time; prerender pays the next page’s TTFB, FCP, and LCP up-front. The tl;dr for my implementation is we add data-prefetch=”prerender” to all top level menu items and data-prefetch (no value) to sub menus. This immediately prerenders top level items and immediately prefetches secondary level items but prerendered on demand. All other links are dormant until they get prefetched on demand. For example: <a href data-prefetch>Prefetched Link</a> <a href data-prefetch=prerender>Prerendered Link</a> It’s great that WP are applying this but on initial viewing it appears the application is a little….thirsty, and potentially underpowered, however I am sure we’ll gain additional tools to extend and improve this on later releases. Currently it appears you can change the eagerness and add URLs paths to an exclusion list.This is what it appears to be doing: One rule targeting all in‑site links (href_matches: “/*”), minus a big exclusion list Single eagerness: conservative (on click) – avoids overload but may under‑prefetch pages Every internal URL except wp‑admin, uploads, no‑prefetch classes, nofollow links This appears a good base to start from, however in the CSS Wizardry post, many of the ‘eagerness’ rules are ‘immediate’, ‘moderate’, WP’s appears to be mostly set as ‘conservative’. Here is the eagerness settings as outlined by a Chrome developers blog post about the Speculation Rules API:immediate: This is used to speculate as soon as possible, that is, as soon as the speculation rules are observed. eager: This currently behaves identically to the immediate setting, but in future, we are looking to place this somewhere between immediate and moderate. moderate: This performs speculations if you hover over a link for 200 milliseconds (or on the pointerdown event if that is sooner, and on mobile where there is no hover event). conservative: This speculates on pointer or touch down. As such the WP approach appears to be a good one for a “set‑and‑forget” broad prefetch on a content site, with prefetch only. However I really liked the CSS Wizardry approach which allows us some control over what get’s prefetched and prerendered. So that said, I’ve decided to disable the new core Speculation rules that comes out of the box on 6.8, via the following: add_filter( 'wp_speculation_rules_configuration', function ( $config ) { // Returning null disables all speculative loading rules. return null; } ); And have applied the data attributes to my menu via WP_HTML_Tag_Processor and walker_nav_menu_start_el if ( ! function_exists( 'dgwltd_speculation_rules' ) ) : function dgwltd_speculation_rules( $item_output, $item, $depth, $args ) { if ( 'primary' === $args->theme_location ) { $tags = new WP_HTML_Tag_Processor( $item_output ); // Add data-prefetch=prerender to top-level menu links // Top-level menu items are immediately prerendered // Sub-menu items are immediately prefetched but prerendered on demand (hover for 200ms) if ( $item->menu_item_parent == 0 ) { $tags->next_tag( 'a' ); $tags->set_attribute( 'data-prefetch', 'prerender' ); } else { $tags->next_tag( 'a' ); $tags->set_attribute( 'data-prefetch', '' ); // Add the attribute without a value } return $tags->get_updated_html(); } return $item_output; } add_filter( 'walker_nav_menu_start_el', 'dgwltd_speculation_rules', 10, 4 ); endif; And the full speculation rules placed in the footer <script type=\"speculationrules\"> { \"prefetch\": [ { \"where\": { \"selector_matches\": \"[data-prefetch]\" }, \"eagerness\": \"immediate\" }, { \"where\": { \"and\": [ { \"href_matches\": \"^/.*\" }, { \"not\": { \"href_matches\": [ \"\\/wp-*.php\", \"\\/wp-admin\\/*\", \"\\/wp-content\\/uploads\\/*\", \"\\/wp-content\\/*\", \"\\/wp-content\\/plugins\\/*\", \"\\/wp-content\\/themes\\/<?php echo sanitize_title($theme_slug); ?>\\/*\", \"\\/*\\\\?(.+)\" ] } }, { \"not\": { \"selector_matches\": \"a[rel~=\\\"nofollow\\\"]\" } }, { \"not\": { \"selector_matches\": \".no-prefetch, .no-prefetch a\" } } ] }, \"eagerness\": \"moderate\" } ], \"prerender\": [ { \"where\": { \"selector_matches\": \"[data-prefetch=prerender]\" }, \"eagerness\": \"immediate\" }, { \"where\": { \"selector_matches\": \"[data-prefetch]\" }, \"eagerness\": \"moderate\" } ] } </script>",
"title": "It’s hard to speculatively load what tomorrow may bring.",
"updatedAt": "2025-04-23T11:34:33.000Z"
}