Reading params deeply in Server Components (e.g. for i18n / multi-tenancy) #58862
Replies: 11 comments 21 replies
-
We have this exact use case, and it is cumbersome. It would be great if we could remove the |
Beta Was this translation helpful? Give feedback.
-
Related: PR #59909 provides a new similar API |
Beta Was this translation helpful? Give feedback.
-
@leerob can you clarify what the thoughts/plans of the team are in regards to this? thanks! |
Beta Was this translation helpful? Give feedback.
-
take a look at https://github.com/vordgi/next-impl-getters/blob/main/src/get-params.ts I think it would be a better stopgap than our |
Beta Was this translation helpful? Give feedback.
-
My conclusion after dealing with internationalization and similar global-but-not-quite values is just accept prop drilling and pass them everywhere. |
Beta Was this translation helpful? Give feedback.
-
I had a quick look into what it'd take to implement this. From my understanding, the RSC render starts here: next.js/packages/next/src/server/app-render/app-render.tsx Lines 949 to 959 in 0b261f0 As far as I understand, all layouts and the page that render as part of a request are all part of the Now the mechanism for "server context" in Next.js is based on next.js/packages/next/src/server/app-render/app-render.tsx Lines 1504 to 1510 in 0b261f0
Since there is only a single render call, I don't see an obvious place where This poses the question: Should React provide a mechanism to distribute data via a context-like mechanism within Server Components since it "owns" the call to render components? The initial I'm not really familiar with the Next.js codebase though, so if anyone has ideas or pointers, I'd be happy to discuss! EDIT: Seems like |
Beta Was this translation helpful? Give feedback.
-
Made a PR with the get-params implementation (in the basic version - it's a very simple change). |
Beta Was this translation helpful? Give feedback.
-
I am still digging this issue, and I feel like we would benefit from a kind of shared context, but that would allow different value client-side and server-side. What led me to this idea:
API would be: import { getSharedIntlContext, SharedIntlContext } from "./Intl"
// A React Server Component layout
async function ServerLayout({children) {
const translations = await getTranslations()
const clientTranslations = filter(translations, clientTokens)
return (
<div>
{/* These translations are only available to server components */}
<SharedIntlContext translations={translations}>
<SomeServerComponent />
<ClientLayout translations={clientTranslations} />
</SharedIntlContext>
</div>
)
}
function ClientLayout({children, translations}) {
// these translations are only available to client components
return <SharedIntlContext translations={translations} children={children} />
}
// Display a translated message
// It will pick either the server context or the client context
// depending on where it's used
// There are 2 contexts, but only one shared accessor function
function SharedT({token}) {
// sharedIntlContext() should work
// client-side and server-side
// but could return a different value
// depending on the environment
const { translate } = getSharedIntlContext()
return <span>{translate(token)}</span>
} The naming is not great but I hope you follow my point. |
Beta Was this translation helpful? Give feedback.
-
would love to see this resolved |
Beta Was this translation helpful? Give feedback.
-
There's a solution for this in nuqs for search params, and it turns out it works just fine for params too. As a bonus point, you get type-safety for non-string params: // /blog/[date]/[slug]/page.tsx
import {
createSearchParamsCache as createParamsCache,
parseAsIsoDateTime,
parseAsString
} from 'nuqs/server'
type PageParams = {
// Those are async in Next.js 15
params: Promise<{ date: string, slug: string }>
}
const paramsCache = createParamsCache({
date: parseAsIsoDateTime,
slug: parseAsString
})
export default async function Page({ params }: PageProps) {
paramsCache.parse(await params)
return (
<Deeply>
<Nested>
<ReactServerComponent />
</Nested>
</Deeply>
)
}
function ReactServerComponent() {
// No prop drilling: reuse the same cache reference
const slug = paramsCache.get('slug')
const date = paramsCache.get('date')
// ?^ Date
} |
Beta Was this translation helpful? Give feedback.
-
Apps that use i18n or multi-tenancy are typically implemented with a top-level dynamic segment like
[locale]
or[market]
(or a combination of both). Currently, to be able to use the value of these segments in Server Components, the only way is to read them in apage
orlayout
from props and pass the values down to the components where they are needed (see e.g. the Next.js i18n example).While this can work for some apps, it can become quite cumbersome if many components are implemented as Server Components and require param values. My personal observation is that especially content-heavy apps can be implemented with a large number of components being Server Components. In the case of i18n, most components require the
locale
argument, therefore the developer would have to pass this value through almost all layers of components.To mitigate this, i18n libraries like
next-intl
andnext-international
have introduced workarounds to pass alocale
as a request header from the middleware to Server Components. While this improves the ergonomics, it opts all pages into dynamic rendering by default. Due to this, these libraries also providecache()
-based workarounds likesetRequestLocale
innext-intl
andsetStaticParamsLocale
innext-international
to be able to bring back static rendering capabilities. While this works, it's arguably a bit cumbersome too and requires special care from the developer.I previously had some hopes that ServerContext could work for this use case, but this API was removed from React. There was a note from @sebmarkbage in the related PR that deprecated the feature though:
It would be very helpful if Next.js would provide a way to read the params that are passed as props to pages and layouts deeply from within the tree. The necessary constraint would be that only params from the current and parent segments can be read (same as the
params
prop).Example API:
Related discussions:
Thank you for your consideration!
EDIT: Here are some notes from an analysis I did on the implementation: #58862 (comment).
Beta Was this translation helpful? Give feedback.
All reactions