Using React's use with Context Provider for data streaming
To stream data from server to Client Component: (1) Fetch data in a Server Component (parent or layout) without awaiting it; (2) pass the Promise down to a Context Provider; (3) the Client Component unwraps it with use() since it cannot await during render. Starting the request on the server before the rest of the app renders lets the response stream immediately and avoids client-side request waterfalls. The Promise can be passed as a single prop or paired with a React context provider so any Client Component can read the value through a custom hook.
Server Component Promise passing to Context Provider example
In app/layout.tsx: import UserProvider and getUser function; in RootLayout, call let userPromise = getUser() without awaiting; return JSX with UserProvider userPromise prop containing userPromise (not awaited); UserProvider component uses 'use client'; imports createContext and useContext; defines UserContext = createContext<Promise<User> | null>(null); exports useUser hook that gets userPromise from context and throws error if not in provider; exports UserProvider component that receives children and userPromise props and returns UserContext.Provider with userPromise value. In Client Component (app/profile.tsx), use 'use client', import use from React and useUser hook, call const userPromise = useUser(), then const user = use(userPromise) to unwrap.
Wrap Promise consumer in Suspense boundary
The component that consumes the Promise (e.g. Profile) suspends while the Promise resolves. Wrap it in a <Suspense> boundary with a fallback to show loading state while the Promise resolves. The component sees streamed, prerendered HTML before JavaScript has finished loading.
SWR integration with Next.js for SPA data fetching
With SWR 2.3.0 and React 19+, you can gradually adopt server features alongside SWR-based client data fetching. SWR supports three modes: Client-only with useSWR(key, fetcher), Server-only with useSWR(key) plus RSC-provided data, and Mixed with both. Use SWR when you need client-side features like revalidation on focus/interval, mutate, or request deduplication across components. If a Client Component only needs to read server data once, pass a Promise and unwrap with use() instead to avoid adding a data-fetching library.
SWR server data seeding with fallback
Wrap application in <SWRConfig> with a fallback object in app/layout.tsx. In fallback, provide key-value pairs where keys are API routes and values are Promises from server-side functions, not awaited. Only components that read the key suspend. Because the Server Component runs on server, getUser() can securely read cookies, headers, or access the database without needing a separate API route. Client components can call useSWR() with the same key to retrieve seeded data. The component code with useSWR does not require changes from existing client-fetching solution.
SWR fallback data advantages
SWR fallback data can be prerendered and included in initial HTML response, then immediately read in child components using useSWR. SWR's polling, revalidation, and caching run client-side only, preserving SPA interactivity. Because Next.js seeds the fallback on the server, useSWR has data on first render without needing conditional logic for undefined data. Seeded data counts as loaded so isLoading stays false. Client-side revalidation surfaces as isValidating, which can show a background-refresh indicator.
SWR vs RSC vs RSC+SWR comparison table
Comparison of data fetching approaches: SSR data - SWR: No, RSC: Yes, RSC+SWR: Yes. Streaming while SSR - SWR: No, RSC: Yes, RSC+SWR: Yes. Deduplicate requests - SWR: Yes, RSC: Yes, RSC+SWR: Yes. Client-side features - SWR: Yes, RSC: No, RSC+SWR: Yes.
Scoping SWRConfig to route segments
<SWRConfig> can live in any Server Component, not only the root layout. Placing it on the route segment that owns the data keeps the fallback close to where it is read, keeps unrelated keys out of a global config, and lets each segment start its own server-side requests. Nested <SWRConfig> providers merge their fallbacks, so a page-level config extends the keys seeded by a parent layout rather than replacing them.
SWR fallback and useSWR key must match exactly
The fallback key and the useSWR key must match exactly since SWR looks up the seeded value by key. Nothing warns on a mismatch: the seeded value is never read, data starts as undefined, and SWR fetches again on the client. When a key is built from dynamic values like route params or search params, derive it in one shared place so server and client cannot drift apart. Fallback seeds the first render, not SWR's persistent cache, so use preload to fill the cache and reuse the request on revalidation.
TanStack Query integration with Next.js
TanStack Query (formerly React Query) can be used with Next.js on client and server, seeding cache from a Server Component like SWR. TanStack Query requires one-time setup: getQueryClient (new per request on server, singleton in browser), <QueryClientProvider>, and client configured to dehydrate pending queries. TanStack Query owns this integration, so follow its Advanced SSR guide for full setup and current APIs. A Server Component starts request with prefetchQuery without awaiting, then serializes cache into streamed HTML with <HydrationBoundary>.
TanStack Query prefetching without awaiting
In a Server Component, call queryClient.prefetchQuery with queryKey and queryFn without awaiting it, so rendering is not blocked. Then return <HydrationBoundary state={dehydrate(queryClient)}> wrapping the client component that will read the data.
TanStack Query useSuspenseQuery vs useQuery
In Client Component, use useSuspenseQuery when a <Suspense> boundary handles loading and you want data to always be defined. Use useQuery when you would rather render its isPending and error states inline. The query key must match the server prefetch exactly.