Preload
Fetch pages before they are visited, so the click feels instant.
Import it from pagefold/preload (about 2.2 KB minified and compressed, shared code included).
import PagefoldPreload from 'pagefold/preload'
const preload = new PagefoldPreload({ preloadVisibleLinks: { delay: 300 } })
new Pagefold({ plugins: [preload] })
Preloaded pages go into the cache, so a later visit makes no request.
What it saves
The click happens at the same moment in all three rows. Without Preload the visitor waits for the request after clicking. With it, the request started earlier, while the pointer rested on the link:
Measured
Time from the click until the new page is on screen, on this documentation site, median of eight pages. The full page load already has the CSS and scripts in the browser cache, so what it pays is the page itself and the checks for the rest:
| Connection | Full page load | Pagefold | Pagefold + Preload |
|---|---|---|---|
| No throttling | 16 ms · 6 requests | 8 ms · 1 request | 12 ms · 1 request |
| Fast 4G | 376 ms · 6 requests | 190 ms · 1 request | 12 ms · 1 request |
| Slow 3G | 912 ms · 6 requests | 565 ms · 1 request | 314 ms · 1 request |
Preload waited 300 ms with the pointer on the link, the default. On Slow 3G that is not enough for the page to arrive, so part of the wait remains: the click joins the request that is already under way.
These are guides, not promises. They come from one browser (Chromium 156), one machine and a local server that sends the same cache headers as Netlify, with the network slowed down by the browser. Run pnpm bench in the repository to measure your own build.
Links need the trailing slash your server uses. If a link goes to /docs/options and the server redirects it to /docs/options/, the page that comes back is stored under a different URL than the one the link names, so neither the cache nor Preload can use it. Link to the final address. See Caching and history.
If the click comes first
If someone clicks a link while its page is still being preloaded, the visit waits for that request instead of starting a second one.
What is skipped
Links to other origins, to the current page, to pages already cached, with data-no-preload, rejected by ignore, or that Pagefold would not handle anyway (target="_blank", data-no-pagefold). A failed preload is ignored: the visit will fetch it normally.
From code
await preload.preload('/pricing') // one URL
await preload.preload(linkElement) // a link
await preload.preload(['/a', '/b']) // several
await preload.preload('/checkout', { priority: true }) // go first in the queue
await preload.preloadLinks(document.querySelector('.menu')) // every link inside
Each returns the page, or undefined if it was skipped or failed.
Hook
page:preload fires after a page is preloaded, with the page: { page }.
Slow connection detection uses the Network Information API, which not every browser has. Where it is missing, preloading simply stays on.
Speculation Rules
Browsers can prefetch pages by themselves if the page has speculation rules. You do not need them with Pagefold: this plugin already puts the page in Pagefold’s own cache, and it does it with any server.
If you use them anyway, know how they meet:
- A page prefetched by the browser is reused by Pagefold only if your server allows caching it, for example with
Cache-Control: private, max-age=60. - Without that header the browser asks again when Pagefold fetches the page, so each page is requested twice.
The second point is easy to miss, because nothing breaks: everything works, with double the requests. Pagefold’s own cache has no such condition.
Common problems
- Each page is requested twice. You also use speculation rules and the server does not allow caching. See above.
- Too many requests. Lower
throttle, turn offpreloadVisibleLinks, or mark links withdata-no-preload. - A page that depends on the visitor looks old. A preloaded page goes into the cache like any other. Keep it out with
ignore, or set a shortcacheTtl. See Caching and history. - Nothing is preloaded on a phone. On slow connections, or when the visitor asked to save data, the plugin does nothing. Turn that off with
respectConnection: falseif you would rather not respect it.
Options
| Option | Type | Default | What it does |
|---|---|---|---|
throttle | number | 5 | Maximum simultaneous requests. |
preloadInitialPage | boolean | true | Fetch the page you are on, so coming back to it is instant. |
preloadHoveredLinks | boolean | true | Preload when a link is hovered, touched or focused. |
hoverDelay | number | 65 | How long the pointer must rest on a link before it is preloaded, in milliseconds. |
preloadVisibleLinks | boolean | object | false | Preload links that scroll into view. Pass an object to set threshold (0.2), delay (500), containers (["body"]) and ignore. |
ignore | (el) => boolean | () => false | Return true to never preload a link. |
respectConnection | boolean | true | Do nothing on slow connections (2G) or when the user asked to save data. |