Pagefold

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:

No preload
pointer on the linkclick, then requestswap + in
Hovered first
hover: page preloadsswap + in
Click comes first
hover: request startssame requestswap + in
click
Third row: the click arrives before the request ends. The visit waits for that request instead of starting a second one.

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:

ConnectionFull page loadPagefoldPagefold + Preload
No throttling16 ms · 6 requests8 ms · 1 request12 ms · 1 request
Fast 4G376 ms · 6 requests190 ms · 1 request12 ms · 1 request
Slow 3G912 ms · 6 requests565 ms · 1 request314 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 off preloadVisibleLinks, or mark links with data-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 short cacheTtl. 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: false if 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.