Server Rendering with fallbackData

Purpose and Scope

This page explains how to combine server-produced data with SWR client revalidation. The official server-render example frames the pattern as a Next.js page that fetches data on the server, passes it into React as props, and then gives that value to SWR through fallbackData. The important behavior is not that the data stays frozen; it is that the first client render can show useful HTML immediately while SWR still owns freshness after hydration. Once the client application starts, SWR can revalidate the same key against the API and update the DOM only if fresher data is returned.

In SWR terminology, fallbackData is per-hook initial data for a key, while fallback on SWRConfig is a keyed map of initial cache values shared by hooks under that provider. The E2E server-prefetch page demonstrates both ideas in the same client component: one hook disables a strict warning, one passes fallbackData: 'SWR', and the provider supplies fallback: { 'ssr:5': 'SWR' }. That page exists to validate server rendering and hydration behavior rather than to serve as a full product example. Sources: e2e/site/app/server-prefetch-warning/page.tsx

The repository also includes source for React Server Component entrypoints. In RSC code, SWR does not expose the browser hook runtime as the main primitive; instead, the server entrypoint exports serialization helpers, SWRConfig, the infinite prefix, and a server-safe preload. This distinction matters when designing a server-rendered route. Server code can prepare cache-shaped data or serialize keys, while client components still call hooks such as useSWR and useSWRMutation after hydration. Sources: src/_internal/index.react-server.ts, src/_internal/utils/server-preload.ts

Relevant Source Files

  • e2e/site/app/server-prefetch-warning/page.tsx - Client-side E2E route that renders during SSR, enables strictServerPrefetchWarning, supplies provider-level fallback, and uses hook-level fallbackData to exercise warning and hydration behavior.
  • src/_internal/index.react-server.ts - React Server export surface for SWR internals, including serialize, SWRConfig, INFINITE_PREFIX, and preload.
  • src/_internal/utils/server-preload.ts - Implements server preload by serializing an SWR key and returning an object whose property name is the serialized cache key and whose value is the fetcher response.
  • e2e/site/app/react-server-entry/page.tsx - Server Component page that imports unstable_serialize from swr and from swr/infinite, proving that server-compatible serialization entrypoints are available.
  • e2e/site/app/mutate-server-action/action.tsx - Server Action returning { result: 10086 } after a delay, used by the mutation E2E route to validate server/client boundaries.
  • e2e/site/app/mutate-server-action/page.tsx - Client page that wraps a Server Action with useSWRMutation, demonstrating that server-rendered applications can still trigger client-side remote mutations after hydration.

Task Flow: SSR Data, Hydration, and Revalidation

A server-rendered SWR page should start by deciding which data must be available for the initial HTML. In a Pages Router application, the official example uses getServerSideProps to fetch data before rendering and then passes that value as a prop. In an App Router or RSC-oriented application, the same architectural decision appears as server code that prepares data or cache entries before a client component mounts. Either way, the server result is not a replacement for the SWR key. The hook still needs the same stable key it will use for client revalidation.

The client component then calls useSWR with that key and a fetcher. If the data was passed directly to the component, use fallbackData in the hook options. If the data is better shared across a subtree, place an SWRConfig provider around the component and put the values in the fallback map. The E2E warning route shows the provider form for key ssr:5 and the hook form for key ssr:4. Both are ways to avoid an empty first render while preserving SWR’s stale-while-revalidate lifecycle. Sources: e2e/site/app/server-prefetch-warning/page.tsx

<SWRConfig value={{ fallback: { 'ssr:5': 'SWR' } }}>
  <Content />
</SWRConfig>

After hydration, SWR decides whether to revalidate according to the hook and global configuration. The key point for server rendering is that the initial value is treated as cached or fallback state, not as proof that the remote resource is permanently fresh. This gives users a fast first paint and gives the application the normal SWR update path once browser effects, focus events, reconnect events, or explicit mutations occur. In the server-prefetch E2E route, the component also tracks hydrated state with useEffect, making the test able to distinguish SSR output from the hydrated client state. Sources: e2e/site/app/server-prefetch-warning/page.tsx

Server Preload and React Server Entry Behavior

The server preload helper is intentionally small and cache-shaped. It accepts a key and a fetcher, calls serialize on the key, and returns an object whose single property is the serialized key. The property value is the fetcher response for the serialized argument. This mirrors how fallback maps are consumed: the server can produce an object keyed by SWR’s internal cache key, and the client-side configuration can make that value available before a hook fetch completes. Sources: src/_internal/utils/server-preload.ts

preload(key, fetcher)
// returns: { [serializedKey]: fetcher(serializedArgument) }

Serialization is the bridge between server-produced data and client hook identity. A string key is simple, but SWR keys can also be functions or other supported key forms. The React Server entrypoint exports serialize, and the E2E RSC page renders the result of unstable_serialize('useSWR'). It also imports unstable_serialize from swr/infinite and calls it with a page-key function, proving that infinite-loading keys have a corresponding server-compatible serialization path. Sources: src/_internal/index.react-server.ts, e2e/site/app/react-server-entry/page.tsx

Use server preload when you want to prepare the same cache key that a client hook will later read, especially when multiple components share the same preloaded result. Use hook-level fallbackData when the initial value belongs to one hook call and you do not need to populate a provider map. In both cases, keep the fetcher contract consistent: the server fetcher used to create initial data and the client fetcher used by useSWR should represent the same resource, return compatible data, and use the same key semantics.

Warning and Boundary Considerations

The server-prefetch-warning route shows that SWR can warn about hooks that render on the server without prefilled data when strictServerPrefetchWarning is enabled. Inside the test route, hooks for ssr:1, ssr:2, and ssr:5 run under a provider with strictServerPrefetchWarning: true; one hook opts out with strictServerPrefetchWarning: false; another avoids the problem with fallbackData; and one gets data through provider fallback. This arrangement documents the intended escape hatches: either provide fallback data, disable the strict warning for a specific case, or use a provider-level fallback for shared data. Sources: e2e/site/app/server-prefetch-warning/page.tsx

Server Actions and mutations live on a different boundary from initial render data, but they are part of the same server-rendered application story. The mutation E2E route marks the page as a client component, imports a server action, and wraps it with useSWRMutation. The server action waits and returns { result: 10086 }; the client hook exposes trigger, data, and isMutating. This demonstrates that initial HTML and server-safe entrypoints do not prevent client-driven writes. Use fallbackData or fallback for first paint, and use mutation hooks for user-initiated changes after hydration. Sources: e2e/site/app/mutate-server-action/action.tsx, e2e/site/app/mutate-server-action/page.tsx

const useServerActionMutation = () =>
  useSWRMutation('/api/mutate-server-action', () => action())

Compact Reference

ConcernUseSource-backed signal
Per-hook initial valueuseSWR(key, fetcher, { fallbackData })The server-prefetch route passes fallbackData: 'SWR' for ssr:4.
Provider initial values<SWRConfig value={{ fallback }}>The server-prefetch route provides fallback: { 'ssr:5': 'SWR' }.
Strict SSR warningsstrictServerPrefetchWarningThe provider enables it globally, while one hook disables it locally.
Server cache preparationpreload(key, fetcher)The helper serializes the key and returns a fallback-shaped object.
Server key identityunstable_serializeThe RSC page renders serialization for core SWR and infinite SWR keys.
Post-hydration writesuseSWRMutationThe mutation route triggers a Server Action from a client component.

To try the official example locally, download the examples/server-render directory, install dependencies with yarn or npm install, and run the development server with yarn dev or npm run dev. When adapting it to your own Next.js route, first choose the stable SWR key, then fetch the initial value on the server, then pass that value through fallbackData or SWRConfig fallback, and finally confirm that the client fetcher revalidates the same resource after hydration.

Read api-swr-config next if you want the full configuration contract for fallback, global fetchers, and callbacks. Read api-preload when you want a focused reference for server-side cache preparation and typed preload behavior. If your route uses Server Components or Suspense, continue with suspense-ssr-and-rsc and troubleshooting-suspense-and-server-rendering, because those pages cover rendering constraints, streaming behavior, and edge cases that are easy to miss when moving from a simple fallbackData example to a production Next.js application.