Basic Data Fetching

Purpose and Scope

This page explains the basic SWR data-fetching pattern through the repository’s introductory hook example and the E2E pages that exercise the same behavior in a Next.js app. The reader problem is intentionally small: render a component, request data from an API-like key, show a loading or empty state while the request is pending, and update the UI when the fetcher resolves. That pattern is the foundation for the official basic example, whose documented intent is to show SWR fetching data from an API in two different pages.

The repository README frames SWR as a React Hooks library for data fetching and introduces the stale-while-revalidate lifecycle: return cached data first, send a request to revalidate, and then update with fresh data. The quick-start snippet uses one hook, useSWR, with a request key and a fetcher function, and reads data, error, and isLoading from the hook result. The E2E basic SSR page uses the same contract in a compact client component, while the render-preload page shows how the basic render path can be combined with preload and Suspense for a prefetch-before-display flow.

Sources: README.md, e2e/site/app/basic-ssr/block.tsx, e2e/site/app/basic-ssr/page.tsx, e2e/site/app/render-preload-basic/page.tsx

Relevant Source Files

  • README.md — introduces SWR, describes the stale-while-revalidate model, and provides the minimal useSWR('/api/user', fetcher) quick-start pattern with data, error, and isLoading.
  • e2e/site/app/basic-ssr/block.tsx — implements the basic client-side SWR block with useSWR<string>('/api/data', async (url: string) => ...), rendering the resulting data or undefined.
  • e2e/site/app/basic-ssr/page.tsx — exposes the basic SSR route by rendering the Block component from the route segment.
  • e2e/site/app/render-preload-basic/page.tsx — demonstrates a related basic render path that calls preload(key, fetcher) before rendering a useSWR consumer and tracks the fetch count.
  • e2e/site/README.md — documents how to run the Next.js E2E site locally and how its pages/api directory maps files to /api/* routes.

These files matter together because the README provides the public API vocabulary, while the E2E pages show that vocabulary in an application route. In the basic block, the key is the API path string '/api/data', the fetcher receives that key as its url argument, and the hook result is rendered directly into the DOM. The page file is deliberately thin, which makes the example easy to reason about: routing concerns stay in page.tsx, and data-fetching concerns stay in the client component.

Sources: README.md, e2e/site/app/basic-ssr/block.tsx, e2e/site/app/basic-ssr/page.tsx, e2e/site/README.md

Core Primitives

The first primitive is the SWR key. A key is the unique identifier for a request and cache entry; in basic examples it is usually the URL of the API being requested. The README says the key is normally the URL of the API, and the basic SSR block follows that recommendation by using '/api/data'. Because the same key identifies the same resource, components that use the same key participate in the same cache and revalidation model rather than each owning completely isolated request state.

The second primitive is the fetcher. A fetcher is any asynchronous function that accepts the key, performs the data access, and returns the value SWR should store. The README explicitly notes that the fetcher can use any preferred data-fetching library. In the E2E basic block, the fetcher calls fetch(url), parses JSON, and returns only res.name, so the hook’s data is typed as a string rather than the entire response object. That small transformation is important: SWR does not require the cached value to match the transport payload exactly.

The third primitive is the hook result. In the README quick start, useSWR returns data, error, and isLoading, allowing the component to branch between failure, loading, and loaded UI. In the E2E block, the component only reads data and renders result:{data || 'undefined'}. That makes the pending state visible without a separate loading branch and gives tests a stable text value while the request is unresolved. Both styles are valid; the more explicit README version is better for user-facing UI, while the E2E version is optimized for observing state transitions.

Sources: README.md, e2e/site/app/basic-ssr/block.tsx

Walkthrough: Fetch API Data in a Page

Start with a client component that imports useSWR from swr. In a Next.js App Router project, the E2E block begins with 'use client' because React hooks and browser-driven fetching run in a client component. The route page then imports and renders that component. This separation is a useful pattern for basic examples: the route remains a server-compatible wrapper, and the data-fetching code is clearly marked as client-side behavior.

'use client'
 
import useSWR from 'swr'
 
export default function Block() {
  const { data } = useSWR<string>('/api/data', async (url: string) => {
    const res = await fetch(url).then(v => v.json())
    return res.name
  })
 
  return <div>result:{data || 'undefined'}</div>
}

When the component first renders, the fetcher may not have completed, so data can be undefined. The README’s quick start recommends handling this with isLoading, returning a loading message until the request finishes. The E2E component instead renders the word undefined, which is useful for verifying the initial state. After the fetcher resolves, SWR rerenders the component with the returned value. That rerender is the basic feedback loop readers should internalize before moving to mutation, pagination, or global configuration.

The E2E site README describes running the local Next.js development server with npm run dev, yarn dev, or pnpm dev, then opening the local app in a browser. It also explains that files under pages/api are mapped to /api/* routes. For the basic pattern, that means a key such as '/api/data' can target an API route in the same application, while the hook code stays transport-agnostic. The hook only knows that the fetcher returns a promise for data.

Sources: README.md, e2e/site/app/basic-ssr/block.tsx, e2e/site/app/basic-ssr/page.tsx, e2e/site/README.md

Implementation Details and Variants

The basic SSR route is intentionally minimal: e2e/site/app/basic-ssr/page.tsx returns <Block /> and delegates data work to the client block. That means the page’s server-rendered shell can exist independently of the client data lifecycle. The pattern is useful when introducing SWR in Next.js because it avoids mixing server data-fetching APIs with SWR’s client hook in the same file. Readers can first verify that a route renders, then focus on the hook contract inside the component that actually needs remote state.

The basic block also records debug history with useDebugHistory(data, 'history:'). That helper is not part of the public SWR API, but its use in the E2E component signals what the test page is meant to observe: successive values of data as SWR moves from an unresolved value to a resolved value. In an application example, the same observation might be represented by a loading skeleton, a retry message, or a final profile card. The underlying hook behavior is the same: SWR owns the async transition and rerenders when state changes.

The render-preload page adds one more primitive, preload, without changing the basic consumer shape. It defines a stable key, a fetcher that increments fetchCount, and a Preload component that calls preload(key, fetcher) inside an effect before rendering children. The page still calls useSWR(key, fetcher) and reads data; the difference is that the request can be started before the consumer is displayed. This is a useful next step after the basic example because it shows that SWR’s cache and request coordination are not limited to the component that reads the data.

The preload variant is wrapped in OnlyRenderInClient and Suspense, then renders data:{data ?? ''} and a fetch-count display. The important lesson is not that every basic example needs Suspense, but that the basic key-plus-fetcher contract composes with more advanced render timing. If data has already been preloaded, the consumer can reuse the in-flight or completed request associated with the same key. If it has not, useSWR can still start the request when the component renders.

Sources: e2e/site/app/basic-ssr/block.tsx, e2e/site/app/render-preload-basic/page.tsx

Run and Verify the Example

For the official standalone basic example, the documented flow is to download the example directory, install dependencies, and start the development server. The same command shape appears across the repository’s examples: use the tarball download, enter the example directory, then run either Yarn or npm. In the E2E site, the local app can be started with the standard Next.js development command set documented in e2e/site/README.md. Once running, visit the relevant route and watch the rendered output move from the initial value to fetched data.

npm run dev
# or
yarn dev
# or
pnpm dev

A correct basic implementation should have a stable key, a fetcher that returns a promise, and a render path that handles the unresolved state. If the UI never updates, first confirm that the key points to a route that exists and that the fetcher returns the transformed value you expect. If the UI flashes an empty value before showing data, that is normal for this basic pattern unless fallback data, preloading, or Suspense-specific behavior is added. If several components use the same key, remember that SWR treats that key as the shared identity for caching and revalidation.

After this page, read the quick-start and useSWR API pages to understand the full hook signature and return values. Then move to the global fetcher example if you want to remove repeated fetcher arguments, or to the preload page if you want to start requests before a component renders. The basic example is deliberately small, but the same key, fetcher, and result-state primitives carry forward into pagination, optimistic mutation, server rendering, and Suspense workflows.

Sources: README.md, e2e/site/app/render-preload-basic/page.tsx, e2e/site/README.md