BT

InfoQ Homepage News UX Patterns: Stale-While-Revalidate

UX Patterns: Stale-While-Revalidate

Bookmarks

Stale-while-revalidate (SWR) caching strategies provide faster feedback to the user of web applications, while still allowing eventual consistency. Faster feedback reduces the necessity to show spinners and may result in better-perceived user experience.

Jeff Posnick explained in a blog entry the rationale behind the stale-while-revalidate caching strategy:

stale-while-revalidate helps developers balance between immediacy—loading cached content right away—and freshness—ensuring updates to the cached content are used in the future.

stale-while-revalidate and the max-age parameters can be set in the Cache-Control of the HTTP response header. A cached response to an HTTP request that is older than max-age (expressed in seconds) is considered stale. In case of a stale response, if the age of the cached response is within the window of time covered by the stale-while-revalidate setting, the stale response is returned in parallel to a revalidation request being performed. The response to the revalidation request replaces the stale response in the cache. If the cached response is older than the window of time covered by the stale-while-revalidate setting, the browser will instead retrieve a response directly from the network and populate the cache with that response.

Google, which improved the speed and performance of Google’s display ads with stale-while-revalidate strategies, explained:

This same technique can be applied to any other scenario where loading scripts as quickly as possible is more important than loading the freshest code.

Support for setting stale-while-revalidate alongside max-age in your Cache-Control response header is available in Chrome 75 and Firefox 68. Browsers without support for stale-while-revalidate silently ignore that configuration value and use max-age.

Developers may also usestale-while-revalidate strategies in single-page applications that make use of dynamic APIs. In such applications, oftentimes a large part of the application state comes from remotely stored data (the source of truth). As that remote data may be changed by other actors, fetching it anew on each request guarantees to always return the freshest data available. Stale-while-revalidate strategies substitute the requirement to always have the latest data for that of having the latest data eventually.

The mechanism works in single-page applications in a similar way as in HTTP requests. The application sends a request to the API server endpoint for the first time, caches and returns the resulting response. The next time the application will make the same request, the cached response will be returned immediately, while simultaneously the request will proceed asynchronously. When the response is received, the cache is updated, with the appropriate changes to the UI taking place.

The stale-while-revalidate strategy thus allows most of the time for instantaneous updates of the user interface, and eventual correctness of the displayed data since fresh response data is displayed as soon as it is available.

The Next.js React application framework provides developers with the SWR hook. The following code provides an example of usage:

// Custom React hook using the useSWR hook to fetch remote data about a user 
function useUser (id) {
  const { data, error } = useSWR(`/api/user/${id}`, fetcher)
  return {
    user: data,
    isLoading: !error && !data,
    isError: error
  }
}

// page component
function Page() {
  return  <div>
    <Navbar  />
    <Content  />
  </div>
}

// child components
function Navbar () {
  return  <div>
    ...
    <Avatar  />
  </div>
}

function Content () {
  const  { user, isLoading }  =  useUser()
  if  (isLoading)  return  <Spinner  />
  return  <h1>Welcome back,  {user.name}</h1>
}

function Avatar () {
  const  { user, isLoading }  =  useUser()
  if  (isLoading)  return  <Spinner  />
  return  <img  src={user.avatar}  alt={user.name} />
}

The previous code showcases a useUser hook that leverages the useSWR hook to fetch remote user data. Two components (Content and Avatar) call the useUser hook, with only one request sent to the API, because they use the same SWR key (that used by useUser) and the request is deduped, cached, and shared automatically.

The useSWR hook can additionally be customized to revalidate its cache when a component is mounted (revalidateOnMount), when the window gets focused (revalidateOnFocus), when the browser regains a network connection (revalidateOnReconnect), at a constant interval (refreshInterval), and at other configurable times (cf. documentation). React’s reactive binding ensures that when the cache is updated with the fresh response, the UI will be updated to reflect the new data.

Tim Raderschad presented in a talk at Svelte Summit 2020 the implementation in Svelte of a similar API cache validation strategy. With Svelte’s usual succinctness, the implementation is only 20 lines and leverages Svelte’s stores:

// ./fetcher file
import { writable } from 'svelte/store'

const cache = new Map();

export function getData(url) {
  const store = writable(new Promise(() => {}));

  if(cache.has(url)){
    store.set(Promise.resolve(cache.get(url)));
  }

  const load = async () => {
    const response = await fetch(url);
    const data = await response.json();
    cache.set(url, data);
    store.set(Promise.resolve(data));
  }

  load();
  return store;
}

Any component can then import the previous function to fetch and cache data:

<script>
  import Error from './Error.svelte'
  import Spinner from './Spinner.svelte'
  import { getData } from './fetcher'

  const response = getData('https://0nzwp.sse.codesandbox.io/')
</script>

<h1>New Fetch</h1>
{#await $response}
  <Spinner />
{:then data}
  <code>{new Date(data).toTimeString()}</code>
{:catch}
  <Error />
{/await}

Svelte’s store binding ensures there again that store updates are propagated to the user interface.

It is worth noting that SWR caching strategies in single-page applications are best used when it is appropriate for the application to temporarily show stale data.

Rate this Article

Adoption
Style

Hello stranger!

You need to Register an InfoQ account or or login to post comments. But there's so much more behind being registered.

Get the most out of the InfoQ experience.

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Community comments

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

Allowed html: a,b,br,blockquote,i,li,pre,u,ul,p

BT

Is your profile up-to-date? Please take a moment to review and update.

Note: If updating/changing your email, a validation request will be sent

Company name:
Company role:
Company size:
Country/Zone:
State/Province/Region:
You will be sent an email to validate the new email address. This pop-up will close itself in a few moments.