Prefetch and Preload
Purpose and Scope
Prefetching is the practice of starting a data request before the component that needs the data is fully on the critical path. Preloading is SWR’s explicit API for starting that request against a key and fetcher so a later useSWR call can reuse the in-flight or cached result. This page explains the prefetch and preload example patterns as developer guidance, then maps them to the repository’s end-to-end fixtures that prove the behavior in client rendering, Suspense, and React Server Component hydration scenarios. Sources: README.md, e2e/site/app/render-preload-basic/page.tsx, e2e/site/app/render-preload-avoid-waterfall/page.tsx
SWR’s baseline model matters for understanding why these patterns work. A useSWR hook is keyed by a unique identifier, usually an API URL, and its fetcher asynchronously returns the data for that key. The README describes SWR as returning cached data first, revalidating, and then updating the UI when fresh data arrives. Prefetching uses the same key discipline: if the speculative request and the eventual hook use the same key and compatible fetcher, the hook can observe the warmed cache rather than initiating all work only after render. Sources: README.md
The official prefetch example presents four user-facing strategies: browser <link preload>, a browser-side fetch followed by mutate outside a component, an effect that warms the next page after render, and a hover handler that fetches before navigation. The supplied source evidence focuses on the preload API and cache hydration cases rather than every browser interaction variant, but the same rule connects them: start the request early and write or associate the result with the SWR key that later readers will use.
Relevant Source Files
README.md— Defines SWR’s stale-while-revalidate model, theuseSWR(key, fetcher)contract, and the primary return values that are affected by cached or preloaded data.e2e/site/app/render-preload-basic/page.tsx— Demonstrates a client page that callspreload(key, fetcher)in an effect before rendering children that use the same key.e2e/site/app/render-preload-avoid-waterfall/page.tsx— Demonstrates preloading two keys before Suspense content reads both values, avoiding a sequential fetch waterfall.e2e/site/app/rsc-unstable-preload/client.tsx— Demonstrates a Suspense client component receivingcacheDatathroughSWRConfigand reading the hydrated cache withuseSWRConfig.e2e/site/app/rsc-unstable-preload-no-suspense/client.tsx— Demonstrates the same cache-data handoff without Suspense, includingisLoading,isValidating, and boundmutatestate.e2e/site/app/rsc-unstable-preload-conditional/client.tsx— Demonstrates conditional React Server Component preload behavior withcacheData, Suspense, and explicit revalidation.
Core Primitives
The main primitives are the SWR key, the fetcher, the cache, preload, mutate, and SWRConfig. The key identifies the logical request. The fetcher performs the asynchronous work. The cache stores the result by key. preload(key, fetcher) starts work ahead of a later render, while mutate can write or revalidate data for a key. SWRConfig can carry scoped configuration and, in the RSC fixtures, accepts cacheData so client components can begin with data prepared outside the client render path. Sources: e2e/site/app/render-preload-basic/page.tsx, e2e/site/app/rsc-unstable-preload/client.tsx
In the basic preload fixture, the page imports useSWR and preload from swr, declares a stable key, and increments fetchCount inside an async fetcher after a short delay. A Preload component calls preload(key, fetcher) from useEffect, then flips local state so its children render. The page’s useSWR(key, fetcher) call uses the same key and fetcher, and the UI exposes both data and fetch-count for the test to observe whether the request was reused rather than duplicated unnecessarily. Sources: e2e/site/app/render-preload-basic/page.tsx
useEffect(() => {
preload(key, fetcher)
setIsPreloaded(true)
}, [])
const { data } = useSWR(key, fetcher)Execution Flow
A practical flow for effect-based preloading is to identify the data a near-future screen will need, define the exact SWR key it will use, and start preload before mounting the expensive or Suspense-bound subtree. The basic fixture waits until preload has been called before rendering its children, which makes the relationship explicit for the test page. In an application, the same idea can be applied when a route becomes likely, a page finishes rendering and can warm the next page, or a user signals intent by hovering over a link. Sources: e2e/site/app/render-preload-basic/page.tsx
The avoid-waterfall fixture shows why preloading is more than a micro-optimization. It declares two keys, render-preload-avoid-waterfall:a and render-preload-avoid-waterfall:b, with separate fetchers that each sleep before returning data. The Preload component starts both requests in the same effect, then renders Suspense content that calls useSWR for both keys. Without early parallelization, nested Suspense or render-time data reads can accidentally turn independent requests into sequential waits. Preloading both keys communicates that the two requests are independent and can be warmed together. Sources: e2e/site/app/render-preload-avoid-waterfall/page.tsx
useEffect(() => {
preload(keyA, fetcherA)
preload(keyB, fetcherB)
setReady(true)
}, [])
const { data: first } = useSWR(keyA, fetcherA, { suspense: true })
const { data: second } = useSWR(keyB, fetcherB, { suspense: true })Suspense changes the rendering symptoms but not the contract. In the avoid-waterfall fixture, Content is wrapped in <Suspense fallback={<div data-testid="fallback">Loading...</div>}>, and each useSWR call opts into { suspense: true }. The fallback gives React something to show if the data is not ready, while preload reduces the chance that the subtree begins its work only after it has already suspended. The component still reads data through normal useSWR calls, so preloading does not require a separate data access path.
RSC and Hydrated Cache Patterns
The React Server Component fixtures demonstrate a related pattern: data can be prepared before the client component runs and passed into SWR through SWRConfig as cacheData. The client root receives cacheData: CacheData<string>, creates a fetcher that records when it is called on the client, and renders a component that reads both data from useSWR and cache.get(key)?.data from useSWRConfig. This verifies that the client hook can see hydrated cache state as well as perform explicit revalidation later. Sources: e2e/site/app/rsc-unstable-preload/client.tsx
The Suspense RSC fixture wraps ClientData in a React Suspense boundary and calls useSWR(key, fetcher, { suspense: true }). Inside the component, the rendered fields separate the user-visible data, the raw cache value, the number of client fetcher calls, and a revalidate button wired to the bound mutate. That shape is useful when diagnosing preload behavior: if hydrated data is present, initial rendering can use it; when the user clicks revalidate, the bound mutation path proves that the normal SWR lifecycle still works after preload. Sources: e2e/site/app/rsc-unstable-preload/client.tsx
The no-Suspense RSC fixture keeps the same cache-data handoff but reads isLoading and isValidating from useSWR. This is the version to compare when a page should not suspend. It shows that preloaded or hydrated data should be reasoned about together with SWR’s loading flags: the UI may have cached data, may be validating in the background, and may still expose a manual mutate button for revalidation. These states help distinguish an empty cache from a populated cache that is merely being refreshed. Sources: e2e/site/app/rsc-unstable-preload-no-suspense/client.tsx
The conditional RSC fixture uses the same client-side shape as the Suspense preload fixture, but exists to validate conditional preload paths. The important application lesson is that conditional prefetching should preserve the same key and cache contract even when a request is started only for selected branches. If a branch preloads data and later renders a useSWR reader for that key, the reader can consume the cache; if the branch does not preload, the hook can still fall back to its normal fetcher behavior. Sources: e2e/site/app/rsc-unstable-preload-conditional/client.tsx
Pattern Reference
| Pattern | When to use it | SWR mechanism | Source-backed signal |
|---|---|---|---|
| Browser link preload | The server-rendered HTML can tell the browser to start downloading a known resource early. | The eventual useSWR reader must use a matching key and fetcher or receive the result through cache population. | Official example guidance; README key/fetcher contract. |
| External fetch plus mutate | Browser code outside React knows data will be needed and can write it before a hook mounts. | Use the same key and populate or revalidate the cache with mutate. | README explains key identity and async fetchers. |
| Effect-based preload | A component has rendered and can warm data for itself, children, or a likely next page. | Call preload(key, fetcher) in an effect before or alongside the later useSWR read. | render-preload-basic/page.tsx. |
| Parallel preload | Multiple independent resources are needed by the same Suspense subtree. | Call preload for each key before rendering readers to avoid request waterfalls. | render-preload-avoid-waterfall/page.tsx. |
| RSC cache handoff | Data is prepared outside the client component and should hydrate SWR state. | Pass cacheData through SWRConfig and read with useSWR or useSWRConfig. | RSC unstable preload client fixtures. |
Use these patterns selectively. The official example notes that real applications do not need every prefetching strategy at once; combining too many can waste bandwidth or complicate cache ownership. Start with the moment where latency is visible to users, then choose the least surprising mechanism that starts the same request earlier. For page navigation, hover prefetch or after-render prefetch can be enough. For Suspense waterfalls, parallel preload calls are a clearer fit. For server-prepared data, SWRConfig cache data keeps client code aligned with SWR’s normal hook API.
Running the Example Locally
The official example can be downloaded and run as a standalone app. This is useful when you want to inspect browser behavior such as link preload, hover prefetch, and component effects rather than only reading the end-to-end fixtures.
curl https://codeload.github.com/vercel/swr/tar.gz/main | tar -xz --strip=2 swr-main/examples/prefetch-preload
cd prefetch-preload
yarn
yarn dev
# or
npm install
npm run devAfter running it, compare the user-facing example to the e2e fixtures on this page. Look for the same invariants: one stable key per resource, an async fetcher, an early request trigger, and a later useSWR reader. Then decide whether your application needs a browser-level hint, an imperative cache write, a React effect, a hover interaction, or an RSC cache handoff. For API-level details, continue with the preload, mutate, SWRConfig, and Suspense pages.