Link prefetching

What is prefetching?

Link prefetching is a strategy to improve the perceived performance of page loading.

We cannot improve on the explanation of prefetching provided by MDN Web Docs. They describe it thus:

Link prefetching is a browser mechanism, which utilizes browser idle time to download or prefetch documents that the user might visit in the near future. A web page provides a set of prefetching hints to the browser, and after the browser is finished loading the page, it begins silently prefetching specified documents and stores them in its cache. When the user visits one of the prefetched documents, it can be served up quickly out of the browser's cache.

Commonly documents and assets are prefetched using a <link> element containing a rel="prefetch" attribute. For example:

<link rel="prefetch" href="/images/my-pic.jpg">

The rel="prefetch" attribute is a 'hint' to the browser that the specified URL should be prefetched. It may or may not be prefetched depending on whether the browser has sufficient idle time to do so before the user actually clicks the link.

This works well for traditional sites where each new URL involves an HTTP request and a page refresh. Performance is improved because prefetched documents and assets are already in the browser cache and so don't need to be fetched at the time that the link is clicked.

In addition to prefetch the rel attribute of the <link> element can take different values depending on the type of prefetching required:

  • dns-prefetch

  • preconnect

  • prefetch

  • subresource

  • prerender

This CSS-tricks article provides a comprehensive overview of these different prefetching options.

Care should be taken when implementing prefetching and consideration should be given to the likelihood that the document or asset will be needed in the near future, i.e. how likely it is that a particular link will be the next one to be clicked.

For JavaScript based SPA applications such as Frontity the situation is slightly different. No additional elements are needed in the markup, and the prefetching is performed with JavaScript. In such cases data is prefetched and stored in the application's "state".

We will describe two methods for implementing link prefetching in Frontity. The first will use the <Link /> component provided by Frontity. The second will demonstrate how to create your own component implementing link prefetching.

Frontity's <Link /> component supports prefetching. The prefetching strategy that it adopts is determined by the value of the property state.theme.autoPrefetch.

The possible values for state.theme.autoPrefetch are:

The value of state.theme.autoPrefetch could then be set in the frontity.settings.js file, for example:

const settings = {
  // Other settings...
  packages: [
    {
      name: "my-theme",
      state: {
        theme: {
          autoPrefetch: "hover", // values: no | hover | in-view | all
        },
      },
    },
    // Other packages...
  ],
};

A component that uses Frontity's <Link /> component should import it from @frontity/components:

import Link from "@frontity/components/link";

Here is a short video demonstrating prefetching with Frontity's <Link> component:

Note that for links present in the content to benefit from the prefetching strategy of the <Link> component the content should be processed by the html2react package. See this page in our tutorial for a detailed example illustrating the steps that need to be taken.

If you wish to create your own link component that also implements auto-prefetching based on the value of a state.theme.autoPrefetch property then a possible implementation could look like this:

import React, { useEffect } from "react";
import { connect } from "frontity";

const Link = ({
  state,
  actions,
  link,
  className,
  children,
  rel,
  "aria-current": ariaCurrent,
  onClick: onClickProp,
}) => {
  // Check if the link is an external or internal link
  const isExternal = link.startsWith("http");

  // Prefetch the link's content when it mounts and autoPreFetch is set to `true`
  useEffect(() => {
    if (!isExternal) {
      if (state.theme.autoPrefetch === "all") actions.source.fetch(link);
    }
  }, []);

  const onClick = (event) => {
    // Do nothing if it's an external link
    if (isExternal) return;

    event.preventDefault();
    // Set the router to the new url.
    actions.router.set(link);

    // Scroll the page to the top
    window.scrollTo(0, 0);

    // if the menu modal is open, close it so it doesn't block rendering
    if (state.theme.isMobileMenuOpen) {
      actions.theme.closeMobileMenu();
    }

    if (onClickProp) {
      onClickProp(event);
    }
  };

  return (
    <a
      // ref={ref}
      href={link}
      onClick={onClick}
      className={className}
      aria-current={ariaCurrent}
      rel={isExternal ? "noopener noreferrer" : rel}
      onMouseEnter={() => {
        // Prefetch the link's content when the user hovers on the link
        if (state.theme.autoPrefetch === "hover" && !isExternal)
          actions.source.fetch(link);
      }}
    >
      {children}
    </a>
  );
};

export default connect(Link);

This uses useEffect to call actions.source.fetch(link) to fetch the data if the value of state.theme.autoPrefetch is "all", and if the value is "hover" then it calls actions.source.fetch(link) to fetch the data when the onMouseEnter event occurs.

Last updated