Next.js Discord

Discord Forum

Avoid prop-drilling in server side rendering

Answered
Toy Fox Terrier posted this in #help-forum
Open in Discord
Toy Fox TerrierOP
Hi all! As a NextJS newbie I'm trying to wrap my head around server-side rendering and app "context"

I use App router with [lang] as the first parameter. Almost all my components need to be language aware so currently I keep tossing the lang-prop from child to child and I want to find a better way. Normally I would use a context provider but that's not an option in SSR, is it? So what's the proper way to define a "context" and avoid prop-drilling in SSR?
Answered by luis_llanes
As far as I know there’s not one “context” solution for server side (only) components.
But if you’re saying you need your components to render on the server.. well even client component render the first time on the server.
The very first HTML you’re getting back from the server includes the client components as well, it works well with performance and SEO issues
View full answer

17 Replies

Pages read the Params from the props => params: Promise<{lang:string}> (a promise in next 15, so it later needs to be awaited)

In client components you can use the “useParams()” hook.

Both will hand you the current language set in the URL instead of prop drilling
You might still need to pass it via props to server components that aren’t a page, those can’t fetch the params automatically in their props (only pages) and they can’t use hooks (useParams) unless you make them a client component
Toy Fox TerrierOP
Thanks for the answer. The page in question uses several components and I want those to be rendered server-side (for performance & SEO reasons). So there is no server-side "context" I could use or implement?
As far as I know there’s not one “context” solution for server side (only) components.
But if you’re saying you need your components to render on the server.. well even client component render the first time on the server.
The very first HTML you’re getting back from the server includes the client components as well, it works well with performance and SEO issues
Answer
- Server components (server only, no hydration) will only run and render on the server, once: either at build time or dynamically when requested.
This server approach serves the HTML ready the first time you request the page.

- Client components (the ones with “use client”) are, also, server rendered the first time, when they reach the browser the client picks up from the server output to hydrate (to make client components interactive and attach event handlers, refs, etc) and re-render.
At this point, both the output from the server and the output on the client are the same.. like a traditional React App with isomorphic SSR.

NOTE: in case you don’t know what Isomorphic SSR is (because I didn’t when I was a noobie), Isomorphic Server Side Rendering it’s a rendering strategy where the App is run first on the server to generate the inicial HTML and serve a “static” (non interactive) page quickly, but along with the HTML it sends the whole JavaScript bundle for the app to run again on the client, effectively to hydrate: this means the page becomes interactive as the event handlers are attached, Effects run and the refs are filled with their values
So if you’re worrying about client components slowing down your app and screwing your SEO, that’s not the whole picture.

Just have in mind that the “use client” directive means: “send the JavaScript bundle to the client” which means that if you have heavy dependencies imported in that file, that will also be shipped to the client..
Toy Fox TerrierOP
Thanks a alot, this clarified many things 🙂
No problem, glad I could help. If you have more questions/issues let me know I’ll gladly help if I can
It takes a little to wrap your head around the new concepts and shift your mental model but once it clicks it all makes sense and you think about just COMPONENTS, not server or client components (I mean it’s important to know the differences and use cases for both, they’re boundaries after all)
Toy Fox TerrierOP
Yeah. I come with a very long "backend API <-> SPA" background and just now tipping my toes into SSR world (well, I guess one could say I'm coming back to it as I started this stuff 20 years ago with Perl CGI and PHP 🙂
Lol I’m the newbie then! I’ve been hard studying web dev for 2 years 🥲 but I happen to know how the modern world works and I’ll gladly share what I know.
At least when it comes to all the new features React 18+/Next.js 13+ and rendering patterns
@json (sidenote): not the topic but it sems you're doing this for internationalization? if so, the options on https://nextjs.org/docs/pages/building-your-application/routing/internationalization would most likely be a better bet than manually implementing this
Toy Fox TerrierOP
hmm.. I looked into this and I know I had some reason I decided not to use it but, for the life of me, I cannot recall what it was 😄 This is a side-project I've been tinkering with for a while already. Might have something to do with how I use Sanity.io and it's i18n.
perhaps it was because not all content is available in all languages and I have some case-by-case logic because of that
@Toy Fox Terrier Thanks for the answer. The page in question uses several components and I want those to be rendered server-side (for performance & SEO reasons). So there is no server-side "context" I could use or implement?
the only server side context thats available in all server components are headers and cookies. you can pre process the urls in a middleware before reaching page.js but that could be expensive and not a good solution if you are aiming for SSG.

There isn't an elegant way to get the current params without prop drilling. But just to add @luis_llanes :
- luckily useParams() supports SSR'd client components
- you can structure your code in a way that makes better composition. Instead of prop drill through many components, just prop drill from parent to children in one swoop:

const i = getIntl(lang)

return (
  <A>
    <B>
      <C>
        <D i={i} />
      </C>
    </B>
  </A>
)


The non-elegant way is to use react's cache to store server side context of one request. Its so unelegant that i am too lazy to write an example.