#How do you handle page specific parts inside static layouts?

40 messages · Page 1 of 1 (latest)

cold pier
#

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)

copper bearBOT
#

🔎 This post has been indexed in our web forum and will be seen by search engines so other users can find it outside Discord

🕵️ Your user profile is private by default and won't be visible to users outside Discord, if you want to be visible in the web forum you can add the "Public Profile" role in id:customize

✅ You can mark a message as the answer for your post with Right click -> Apps -> Mark Solution
(if you don't see the option, try refreshing Discord with Ctrl + R)

dim tartan
#

Why don't just pass the data from a server component?

cold pier
#

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 🙈

junior crystal
#

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)

dim tartan
# cold pier I don't know what you mean. Could you please explain? I cannot pass the page's ...

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
cold pier
# dim tartan No, you can't access the pathname from the layout. Some common workarounds: 1....

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.

cold pier
cold pier
dim tartan
# cold pier Thank you for your response. Regarding 1. I have some routes that are in a catc...
  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)

junior crystal
#

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)

cold pier
# dim tartan 1. That's totally wrong. Unless you wrap the server component in a `Suspense`, N...
  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.

  2. 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.

cold pier
dim tartan
cold pier
#

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?

junior crystal
#

"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...)

cold pier
#

Thank you for the insights. This clears up a lot of things I was unsure about.

cold pier
junior crystal
#

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

cold pier
#

Ah, okay. Yeah, that's also a nice use case.