Next.js Discord

Discord Forum

How do you handle page specific parts inside static layouts?

Unanswered
South Polar Skua posted this in #help-forum
Open in Discord
South Polar SkuaOP
Suppose you have a root layout with a header, footer, and main. The header includes the whole navigation logic and the footer secondary navigations and other sections. The page is rendered inside main, while the header and the footer are mostly static.

Now, what do I mean by mostly... A few parts are dependent on the current route or page data.
- The currently active navigation item should be marked as active.
- A language switcher component (potentially deeply nested in the header/footer) needs links for other versions of the same page (e.g. translated URL segments).

The first case should be trivial, because you can just use usePathname and match it with the navigation item's href.

However, the second case needs data from the backend (the translated URLs of the current page), which is not straightforward.
Can you somehow SSR and cache the language switcher? How would you do this without having to do a client side fetching? (Client side fetching is worse UX than if it's just a server component that is loaded based on the page data you already fetch for each page)

39 Replies

Why don't just pass the data from a server component?
South Polar SkuaOP
I don't know what you mean. Could you please explain?
I cannot pass the page's parameters to my layout components, correct?

A bit of mock code, that should show better what I mean:

layout.tsx
const RootLayout = ({ children }) => {
  return (
    <html>
      <body>
        <header>
          <multi>
            <level>
              <nesting>
                <in>
                  <many>
                    <files>
                      <AComponentThatNeedsPageData data="some-page-data" />
                    </files>
                  </many>
                </in>
              </nesting>
            </level>
          </multi>
        </header>

        <main>{children}</main>

        <footer>
          ...
        </footer>
      </body>
    </html>
  );
};


How can I pass data from the page.tsx to the layout? Or how can I make AComponentThatNeedsPageData a server component that has access to the current route?

I feel like I'm missing something very obvious 🙈
We had the same issue for the state of js survey
you can see our current code structure here
If I remember correctly we have moved stuff to page level and accepted repetition
layouts in Next 13 tend to be quite static, it's hard to make them rerender => you cannot tell a layout to rerender on page change (and thus take the current page into account)
they are the complete opposite of client-side layouts that we are used to in React
while the ideal solution would be somewhere in between
(our issue in the state of is that we must display breadcrumbs but only for non generic page or smth like that, it's not me who implemented this so the code is the source of truth)
@fuma No, you can't access the pathname from the layout. Some common workarounds: 1. Move it to a page, as Eric mentioned. You can extract the logic to a `Header` component and add it to every page 2. Turn it into a client component, I can't agree with your perspective about "client-side fetching = worse UX" By using libraries like SWR/React Query, with a Skeleton component everything looks great. And it's how web apps do
South Polar SkuaOP
Thank you for your response.

Regarding 1.
I have some routes that are in a catch-all. Also, I don't know at build time what paths will be available during the lifetime of a release. Wouldn't this approach cause the header to be delayed until the whole page is cached together with the content? I.e. header, footer are delayed because the content is still being SSRd. Comparable to getStaticProps but with blocking as fallback.

2.: Yeah, I guess that's the only solution if 1 has the above described behavior. What I meant with worse UX: I would love to not have this, imo, unnecessary loading state, since it could in theory be cached for every route and SSRd. But it's a solution nonetheless.
@Eric Burel while the ideal solution would be somewhere in between
South Polar SkuaOP
I agree. I think Next.js allowing the page to pass more than children to the layout could be a good solution or something similar.
@Eric Burel https://github.com/Devographics/Monorepo/tree/main/surveyform/src/app/%5Blang%5D
South Polar SkuaOP
Thank you! I'll check it out 🙂
@South Polar Skua Thank you for your response. Regarding 1. I have some routes that are in a catch-all. Also, I don't know at build time what paths will be available during the lifetime of a release. Wouldn't this approach cause the header to be delayed until the whole page is cached together with the content? I.e. header, footer are delayed because the content is still being SSRd. Comparable to `getStaticProps` but with `blocking` as `fallback`. 2.: Yeah, I guess that's the only solution if 1 has the above described behavior. What I meant with worse UX: I would love to not have this, imo, unnecessary loading state, since it could in theory be cached for every route and SSRd. But it's a solution nonetheless.
1. That's totally wrong. Unless you wrap the server component in a Suspense, Next.js will wait for all components to be rendered before sending them to the client.
So putting the switcher as a part of the page can fix the problem.

2. Unfortunately, due to the design of Next.js, your layout is being rendered before the page. Layouts won't be re-rendered when navigating between pages. so it's impossible for now.
I won't say it's unnecessary.

I have read your question again, in your case, you want to pass the translated URL of the current page. Why don't consider passing translated URLs of all the pages? So that you can pass it to the client component. (if you don't have over millions of pages)
Note that we have i18n with local switcher in the header in the surveyform I've linked so perhaps this adress your issue too
I don't think this was a problem regarding layouts/SSR, but we also do a hard refresh on language change too
You cannot access pathname in layouts but you can access route parameters
so you can also introduce an URL rewrite or redirect in the middleware that sets up a route parameter
(rewrite if you want end user to NOT see the param, redirect if you do, that depends on your context)
@fuma 1. That's totally wrong. Unless you wrap the server component in a `Suspense`, Next.js will wait for all components to be rendered before sending them to the client. So putting the switcher as a part of the page can fix the problem. 2. Unfortunately, due to the design of Next.js, your layout is being rendered before the page. Layouts won't be re-rendered when navigating between pages. so it's impossible for now. I won't say it's unnecessary. I have read your question again, in your case, you want to pass the translated URL of the current page. Why don't consider passing translated URLs of all the pages? So that you can pass it to the client component. (if you don't have over millions of pages)
South Polar SkuaOP
1. Ah, thanks. So for the user it doesn't matter if the header component is in the page or the layout?
Can I think of the layout as something that "just" allows you to not repeat yourself in page.tsx all the time, but it has no influence regarding UX? As a rule of thumb, I mean. Of course, there's the whole nested-layouts thing that is awesome - but that's something for another day.

3. Yeah, that's a possibility I've considered. I haven't thought of the performance implications yet, and don't know yet if it's even possible with the CMS API and its architecture, without running into limits, etc. etc.
@Eric Burel You cannot access pathname in layouts but you can access route parameters
South Polar SkuaOP
Sorry for this question: Do you mean the dynamic URL segments or do you mean the search parameters? I'm never a 100% sure in Next.js 😅
South Polar SkuaOP
You mean performance in loading time for the user because the rendered layout (including the header) can be cached on the server and doesn't have to be rendered all the time, as opposed to the header being inside the page and has to be potentially SSRd on a new page navigation because the page isn't cached yet?
"Can I think of the layout as something that "just" allows you to not repeat yourself in page.tsx all the time, but it has no influence regarding UX? As a rule of thumb, I mean. Of course, there's the whole nested-layouts thing that is awesome - but that's something for another day."
Yeah that's the issue: that's what a layout is usually. In React precisely, this means "it doesn't remount on page change". But in Next.js, this also means "it doesn't rerender on page change unless the route parameters did change or where invalidated somehow"
the thing is that we don't have enough invalidation mechanism so you cannot force a rerender after a mutation (update some value) + you cannot force rerendering when navigating without parameter changes
Aside from UI, you can typically use Next.js layout to fetch generic values and put them into a client context
Honestly at this point I am not even sure if we can implement a layout that do not remount on route change in Next, you either have the built-in ultra-static layouts OR replicating the layout per-page but this means it may remount (= losing state) on route change
(I hope Next 14 or 15 will bring more flexibility on that cause I've also hit other issues like you cannot setup a layout to do some static data fetching while some nested pages do either more static data fetching OR dynamic data fetching, if one page does dynamic data fetching then the layout cannot do static fetching while it would have been more intuitive to allow that...)
South Polar SkuaOP
Thank you for the insights. This clears up a lot of things I was unsure about.
@Eric Burel (I hope Next 14 or 15 will bring more flexibility on that cause I've also hit other issues like you cannot setup a layout to do some static data fetching while some nested pages do either more static data fetching OR dynamic data fetching, if one page does dynamic data fetching then the layout cannot do static fetching while it would have been more intuitive to allow that...)
South Polar SkuaOP
Yeah, there are still some weird situations you find yourself in. And I haven't even used most of the feature extensively yet.
Are maybe parallel routes something we could use to render page dynamic stuff inside the layout? It probably needs to be passed down until it's used.
parallel route are more for cases where you don't want to import anything from the route you don't display
like auth vs non-auth version => the layout picks the right one and the other one will never reach client-side
it's a server-side equivalent to a good old "if" in your layout computed during render (in this case you would still have to load 2 components even if you show only 1 at runtime)
They are similar to dynamic imports in my mind but extended to RSC
@Eric Burel you still lack the pathname to pick what you render
South Polar SkuaOP
Yeah makes sense. 🤦‍♂️
Ah, okay. Yeah, that's also a nice use case.
Anyways. Thanks so much to the both of you. I appreciate this a lot!
I have a few solutions or workarounds I can try and choose whatever feels best for the user. 🙂
I hope that Next.js has some functionality in future versions to pass something to the layout from props or similar. This would be great. I also can't imagine I'm the only one 😅