Stale-While-Revalidate Caching in JavaScript

The stale-while-revalidate (SWR) caching strategy optimizes web performance and user experience by serving cached (stale) content immediately while asynchronously fetching updated (fresh) data in the background. This article explains how the pattern works, its primary benefits, and how to implement it in JavaScript using native Service Workers and client-side data-fetching libraries.


What is the Stale-While-Revalidate Pattern?

Stale-while-revalidate is a cache invalidation strategy that eliminates latency for the end user. When a client requests a resource:

  1. Stale Response: The application immediately returns the cached version of the data, regardless of whether it might be slightly outdated.
  2. Revalidation: Simultaneously, a network request is dispatched in the background to fetch the latest version of the data from the server.
  3. Cache Update: Once the server responds, the cache is updated with the fresh data, and the UI can optionally update to reflect the changes.

This approach balances two competing goals: ultra-fast load times (zero network wait time on subsequent visits) and eventual data consistency.


HTTP Header Implementation

Before exploring JavaScript implementations, it is worth noting that stale-while-revalidate is natively supported by modern browsers and CDNs via the standard HTTP Cache-Control header:

Cache-Control: max-age=60, stale-while-revalidate=300

Implementing in JavaScript with Service Workers

In client-side JavaScript, you can implement this pattern manually using the Cache API inside a Service Worker. This is ideal for static assets or offline-first progressive web apps (PWAs).

self.addEventListener('fetch', (event) => {
  event.respondWith(
    caches.open('dynamic-cache-v1').then(async (cache) => {
      // 1. Check for cached version
      const cachedResponse = await cache.match(event.request);

      // 2. Fetch fresh data in the background to update the cache
      const fetchPromise = fetch(event.request).then((networkResponse) => {
        if (networkResponse.status === 200) {
          cache.put(event.request, networkResponse.clone());
        }
        return networkResponse;
      });

      // 3. Return cached response immediately, or fallback to the network
      return cachedResponse || fetchPromise;
    })
  );
});

Implementing in JavaScript with Client-Side Libraries

For dynamic API requests and state management, JavaScript frameworks commonly rely on dedicated libraries designed around this pattern.

1. Vercel’s SWR Library (React)

The swr package popularized this pattern for component-level data fetching:

import useSWR from 'swr';

const fetcher = (url) => fetch(url).then((res) => res.json());

function UserProfile({ userId }) {
  // Returns cached data instantly, then triggers revalidation
  const { data, error, isValidating } = useSWR(`/api/user/${userId}`, fetcher);

  if (error) return <div>Failed to load</div>;
  if (!data) return <div>Loading...</div>;

  return (
    <div>
      <h1>{data.name}</h1>
      {isValidating && <span>Updating in background...</span>}
    </div>
  );
}

2. TanStack Query (React Query)

TanStack Query implements the stale-while-revalidate model by default through its staleTime and gcTime configurations:

import { useQuery } from '@tanstack/react-query';

function ProductList() {
  const { data, isFetching } = useQuery({
    queryKey: ['products'],
    queryFn: () => fetch('/api/products').then((res) => res.json()),
    staleTime: 1000 * 60, // Data remains fresh for 1 minute
  });

  return (
    <div>
      <ul>{data?.map((p) => <li key={p.id}>{p.title}</li>)}</ul>
      {isFetching && <p>Background refresh in progress...</p>}
    </div>
  );
}

When to Use Stale-While-Revalidate

The pattern is ideal for: - User profile data, avatars, and configuration settings. - Social media feeds and public comments. - Product catalogs and search results.

It is not recommended for: - Real-time financial or stock transaction data. - Payment checkouts or inventory reservation steps where strict, immediate consistency is required.