avoid Dynamic Routes prop-drilling?
Unanswered
Kuchto47 posted this in #help-forum
Kuchto47OP
Hey there. I am quite new to the NextJS world (came here from building SPA react apps for couple years), im still essentially learning the basics.
I am using Next 15 with React 19, Typescript and app router.
I do understand the Dynamic Route thing, how to read the dynamic part either in layout.tsx or page.tsx.
What i lack to see is some convenient way, how to read the dynamic route's value in nested server component without propdrilling (passing the value through series of nested components from page.tsx to use it)
Example:
pathname: /a/[coolValue]/b
/a/[coolValue]/b/page.tsx takes in props: { params: Promise<{ coolValue: string }> } and i can get the value e.g. like this: const { coolValue } = await props.params;
Now imagine page.tsx renders <NestedServerComp1/>, which renders <NestedServerComp2/>. I want to read the coolValue inside <NesteServerComp2/>, but i dont want to do <NestedServerComp1 coolValue={coolValue}/> and then, inside, <NestedServerComp2 coolValue={props.coolValue}/>.
Is there some pattern or some key information from documentation I am missing? How do you usually solve such use-cases trying to keep the code clean?
Thanks for any responses/suggestions!
I am using Next 15 with React 19, Typescript and app router.
I do understand the Dynamic Route thing, how to read the dynamic part either in layout.tsx or page.tsx.
What i lack to see is some convenient way, how to read the dynamic route's value in nested server component without propdrilling (passing the value through series of nested components from page.tsx to use it)
Example:
pathname: /a/[coolValue]/b
/a/[coolValue]/b/page.tsx takes in props: { params: Promise<{ coolValue: string }> } and i can get the value e.g. like this: const { coolValue } = await props.params;
Now imagine page.tsx renders <NestedServerComp1/>, which renders <NestedServerComp2/>. I want to read the coolValue inside <NesteServerComp2/>, but i dont want to do <NestedServerComp1 coolValue={coolValue}/> and then, inside, <NestedServerComp2 coolValue={props.coolValue}/>.
Is there some pattern or some key information from documentation I am missing? How do you usually solve such use-cases trying to keep the code clean?
Thanks for any responses/suggestions!
26 Replies
American black bear
some state management libraries sometimes is useful
try learning about zustand, jotai, redux, etc
try learning about zustand, jotai, redux, etc
Kuchto47OP
don't they sort-of force you into client components? i understood that using such state management libs as well as react built-in context is forcing your component to become client component which i wanted to avoid because of SEO friendliness... I have experience with both React Context as well as Redux (toolkit), and i played a little with MobX, but all of those experiences were back in the SPA days, and when i tried Context i quickly realized everything beneath a context provider must be a client component (or am i wrong?) - and since all to-me-known state management libraries are based on react's context, i assumed same limitations i am facing with context would i face with state mngmnt lib...
The issue you're are facing is not solvable using Server Component without prop drilling, if possible I would recommend making the component a client component and calling
useParams to read [coolValue] that way, it is the best solution for a cleaner implementation, but it might not be viable if you REQUIRE that component be a Server Component.In your case the prop drilling looks extremely minimal (only two components) that I would just drill them, it's not that big of a deal.
@Kuchto47 Hey there. I am quite new to the NextJS world (came here from building SPA react apps for couple years), im still essentially learning the basics.
I am using **Next 15** with **React 19**, **Typescrip**t and **app router**.
I do understand the Dynamic Route thing, how to read the dynamic part either in *layout.tsx* or *page.tsx*.
What i lack to see is some convenient way, how to read the dynamic route's value in nested server component without propdrilling (passing the value through series of nested components from *page.tsx* to use it)
Example:
pathname: */a/[coolValue]/b*
*/a/[coolValue]/b/page.tsx* takes in **props: { params: Promise<{ coolValue: string }> }** and i can get the value e.g. like this:** const { coolValue } = await props.params;**
Now imagine *page.tsx* renders **<NestedServerComp1/>**, which renders **<NestedServerComp2/>**. I want to read the *coolValue* inside **<NesteServerComp2/>**, but i dont want to do** <NestedServerComp1 coolValue={coolValue}/>** and then, inside, **<NestedServerComp2 coolValue={props.coolValue}/>**.
Is there some pattern or some key information from documentation I am missing? How do you usually solve such use-cases trying to keep the code clean?
Thanks for any responses/suggestions!
Seems like you're looking for a Server Side Context API and sadly that does not exist.
If you want SEO, keep your pages Server Components, when you need patterns that are solved for Client Components you can compose React Components both server and client to achieve what you are looking for
If you want SEO, keep your pages Server Components, when you need patterns that are solved for Client Components you can compose React Components both server and client to achieve what you are looking for
@Plague The issue you're are facing is not solvable using Server Component without prop drilling, if possible I would recommend making the component a client component and calling `useParams` to read [coolValue] that way, it is the best solution for a cleaner implementation, but it might not be viable if you REQUIRE that component be a Server Component.
But this is true, if you want it to look clean, then create a Client Component and use "useParams()". You don't need to turn the whole page a client component, just the little piece that actually needs to be.
Use whatever composition pattern that works for you, that's the power of React
Use whatever composition pattern that works for you, that's the power of React
* ignore the () around “use client” I wrote everything in a single file and those “()” were added automatically and I didn’t notice lol
Kuchto47OP
thx guys, thx for all the inputs. appreciate it
Sure, let us know which approach you ended up taking, when you do
Kuchto47OP
Sure. So far it seems a little prop drilling is the price to pay, in case i come to it being too deep/unmanagable, i will refine the approach with the inputs from you guys.
I wonder if you could clear one doubt from my head still: I assume i understand it correctly, that any child component of a client component is forced to be client component as well... or is it possible to do <ClientComponent><ServerComponent/></ClientComponent>? Till now i have the impression any children of client component must be client components themselves. Or in practice, if i resort to e.g. wrapping whole app in the root layout with e.g. Context Provider, which must be client component, i implicitly make the whole app to be client component since whole app is a child of this Context Provider, which is client....right?
If it is otherwise i think i will have to go back to the basics, not understanding the fundamentals of Next 😄
I wonder if you could clear one doubt from my head still: I assume i understand it correctly, that any child component of a client component is forced to be client component as well... or is it possible to do <ClientComponent><ServerComponent/></ClientComponent>? Till now i have the impression any children of client component must be client components themselves. Or in practice, if i resort to e.g. wrapping whole app in the root layout with e.g. Context Provider, which must be client component, i implicitly make the whole app to be client component since whole app is a child of this Context Provider, which is client....right?
If it is otherwise i think i will have to go back to the basics, not understanding the fundamentals of Next 😄
Pekingese
@Kuchto47 you can solve this with composition (
if
now you can render
children) and then you won't need to prop-drill:export default async function Page(params) {
return (
<ServerComponent1 coolValue={params.coolValue}>
<ServerComponent2 coolValue={params.coolValue} />
</SeverComponent1>
);
}if
ServerComponent1 renders lots of stuff and you need to slot ServerComponent2 into a specific position, you can use a custom slot prop instead of children:export default async function Page(params) {
return (
<ServerComponent1
coolValue={params.coolValue}
title={<ServerComponent2 coolValue={params.coolValue} />}
>
Some children
</SeverComponent1>
);
}now you can render
title wherever you want inside 🙂Pekingese
or is it possible to do <ClientComponent><ServerComponent/></ClientComponent>
i recommend reading this blog post https://julesblom.com/writing/parents-owners-data-flow. it is possible to do what you describe, but it depends on owner vs parent tree (the blog post clears that up). if you are rendering those components inside a
"use client" component then it will not work, the owner has to be a server component for it to work.@Kuchto47 Sure. So far it seems a little prop drilling is the price to pay, in case i come to it being too deep/unmanagable, i will refine the approach with the inputs from you guys.
I wonder if you could clear one doubt from my head still: I assume i understand it correctly, that any child component of a client component is forced to be client component as well... or is it possible to do <ClientComponent><ServerComponent/></ClientComponent>? Till now i have the impression any children of client component must be client components themselves. Or in practice, if i resort to e.g. wrapping whole app in the root layout with e.g. Context Provider, which must be client component, i implicitly make the whole app to be client component since whole app is a child of this Context Provider, which is client....right?
If it is otherwise i think i will have to go back to the basics, not understanding the fundamentals of Next 😄
Components become “Client conponents” when they’re declared (
If you pass a Server component through props, in this case through
function Component() {} ) and when they’re imported ( import Component from ‘./…’) inside a boundary marked with “use client”.If you pass a Server component through props, in this case through
children, this won’t make them Client components because they’re not for the client component to own ( you didn’t declare them in the “use client” boundary or imported it in there) , for the Client component children is just a slot that will be filled with whatever you pass to it, it does not care about what you’re putting there, just holds a reference to a value that will come laterWhen you wrap your whole app in a Provider, that’s a client component, you’re effectively filling the Providers
children slot with your app, but the provider does not own your app so it’ll just take whatever you pass to it and render itThere are two types of tree you can think of now:
the render tree and the module dependency tree (https://react.dev/reference/rsc/use-client), in the Provider example it would look something like this:
RENDER TREE:
<Provider>
<Layout>
<Page>
<ServerComponent/>
<ClientComponent/>
</Page>
</Layout>
</Provider>
———————————-
MODULE DEPENDENCY TREE:
- rendered on the server:
<Layout>
<Page>
<ServerComponent/>
<ClientComponent/>
(client components also rendered on the server the first time in Next.js)
</Page>
</Layout>
- rendered on the client
<Providers>
{children}
</Providers>
<ClientComponent/>
the render tree and the module dependency tree (https://react.dev/reference/rsc/use-client), in the Provider example it would look something like this:
RENDER TREE:
<Provider>
<Layout>
<Page>
<ServerComponent/>
<ClientComponent/>
</Page>
</Layout>
</Provider>
———————————-
MODULE DEPENDENCY TREE:
- rendered on the server:
<Layout>
<Page>
<ServerComponent/>
<ClientComponent/>
(client components also rendered on the server the first time in Next.js)
</Page>
</Layout>
- rendered on the client
<Providers>
{children}
</Providers>
<ClientComponent/>
Kuchto47OP
this is extremely helpful! thanks a lot i cannot express how thankful i am!
Sure! Glad to help 😉
This community is awesome
@Plague This community is awesome
True, I only joined like 2 weeks ago and I can already tell, it's so good!
Did that solve your issue?
Kuchto47OP
Hey guys. I came back to thank you for your inputs. I did quickly learn a lot in the past weeks, prop drilling in server components is in my past (fast but short) expreince acceptable enough, and SSR SEO-needed content with CSR control elements is an absolute blast. Just recently i created my custom embla-carousel based carousel, generic enoughto accept any type of content and generic enough to have various possibilities of controlling it, like next/prev buttons within carousel for "picture gallery" as well as "header" with next/prev buttons for controlling container of content cards (whatever the content is). I did test it too and confirmed the content of the carousel is rendered on the server thus crawlable easily and the controling bit is CSR as part of hydration...love it, appreciate your inputs, peace!
Glad to hear that, composition is the true power of React.
Btw If the solution was found on this thread please mark it so other people can easily find it too!
Btw If the solution was found on this thread please mark it so other people can easily find it too!