-
Notifications
You must be signed in to change notification settings - Fork 1.3k
Description
Summary
The current SWR docs for Next.js Server Components show how to pass data into <SWRConfig value={{ fallback }}> as promises. However, there’s no documented (and possibly no supported) way to initiate server-side prefetching when the SWR key is dynamic, e.g. derived from searchParams for pagination/filtering.
I would argue that such pattern in is crucial for Nextjs (and even bare minimum for such library), especially when Nextjs 16 is pushing Cache Components + Partial Prerendering.
Docs reference: https://swr.vercel.app/docs/with-nextjs#prefetch-data-in-server-components
What the docs currently cover (static keys)
import { SWRConfig } from 'swr'
export default async function Layout({
children,
}: {
children: React.ReactNode
}) {
// Initiate the data fetching on the server side.
const userPromise = fetchUserFromAPI()
const postsPromise = fetchPostsFromAPI()
return (
<SWRConfig
value={{
fallback: {
// Pass the promises to client components.
'/api/user': userPromise,
'/api/posts': postsPromise,
},
}}
>
{children}
</SWRConfig>
)
}
Problem: no clear path for dynamic keys
In many pages (pagination, filtering, sorting), the SWR key is built from searchParams, for example:
• buildKey(searchParams)
• ['/api/items', { page, sort, q }]
• getKey(index) for infinite/pagination
There’s currently no documented way to prefetch on the server and provide fallback when the key can’t be expressed as a static string.
Current workaround
Right now, I’m not using <SWRConfig> for fallback in this case. Instead, I manually pass a promise to the client component and feed the resolved value into SWR:
export default function Page({ searchParams }: { searchParams: any }) {
const dataPromise = fetchData(searchParams)
return (
<Suspense>
<ClientComponent dataPromise={dataPromise} />
</Suspense>
)
}
'use client'
import useSWR from 'swr'
import { useSearchParams } from 'next/navigation'
import { use } from 'react'
const ClientComponent = ({ dataPromise }: { dataPromise: Promise<any> }) => {
const searchParams = useSearchParams()
const fallback = use(dataPromise)
const { data } = useSWR(buildKey(searchParams), fetcher, { fallback })
// ...
}
This works, but:
• it’s repetitive (must be implemented per-hook/per-component),
• it defeats the point of a global <SWRConfig> hydration.
Proposed solution
Allow passing a fallback promise into <SWRConfig>, where the promise resolves to the fallback map:
import { SWRConfig } from 'swr'
export default function Page({ searchParams }: { searchParams: any }) {
const fallbackPromise = fetchData(searchParams) // resolves to { [dynamicKey]: data }
return (
<SWRConfig
value={{
fallback: fallbackPromise,
}}
>
{children}
</SWRConfig>
)
}
Where fallbackPromise resolves to something like:
// example shape
{
"dynamicKey": { /* data */ }
}
Any thoughts?