Reputation: 796
I have a file architecture as follows:
projects
|
-page.tsx
-[id]
|
-template.tsx
-overview
|
-page.tsx
-settings
|
-page.tsx
I am using server-rendered pages.
I want to get an use the id
dynamic route property in the template.tsx
file. This works fine for layout.tsx
, but I need to use template.tsx
so that I can re-render the sub-pages on route-change.
It seems that id
gets passed as a prop just fine to a layout file, but doesn't get passed to a template file. How can I reference id
inside of my template.tsx
file?
Upvotes: 1
Views: 85
Reputation: 5514
If you need this ID, presumably it's for something dynamic. If that's the case you should probably not put it in template at all.
Why?
The whole point of layout.tsx
and template.tsx
is to allow you to create UI that is shared between routes.
A template
specifically makes sure that the children are revalidated on route change. For example, so that your Suspense
boundaries can display skeleton placeholders again for data that is still loading.
So what to use?
Use a regular Server Component (or Client Component) instead.
Example:
Page component (Server Component)
export default function Page({ params }) {
const { id } = params;
// Do not await, just pass the promise
const dataPromise = fetchDataForId(id);
return (
<main>
<h1>{id}</h1>
{/* Pass the promise directly to the component */}
<MyServerComponent dataPromise={dataPromise} />
</main>
);
}
Dynamic component (Server Component)
// app/[id]/MyServerComponent.tsx
import { use } from 'react';
type MyServerComponentProps = {
dataPromise: Promise<any>;
};
export default function MyServerComponent({ dataPromise }: MyServerComponentProps) {
// Use `use` to suspend until the dataPromise resolves
const { id } = use(dataPromise);
return (
<div>
<h2>{id}</h2>
</div>
);
}
Example output:
To illustrate this, here's some simplified output (not code).
// Pseudo, not actual code
<Layout> /* server component */
<Template> /* server component */
<Page> /* server component */
<h1>{id}</h1>
<SomeOptionalClientComponent>
<SomeServerComponent id={id}>
</SomeOptionalClientComponent>
</Page>
</Template>
</Layout>
In short, why?
- As long as you use composition, each nested
Server Component
will be rendered on the server, even if a parentClient Component
exposes thatServer Component
as children.- The
Template
then does not delay the initial payload based on dynamic data. Which is crucial for page load speed (as it allows the client to start loading fonts, scripts and hydrate, while the server streams in the rest).- This is based on the the intended signature for partial prerendering (PPR) as explained by Next.js core dev Wyatt Johnson https://youtu.be/WLHHzsqGSVQ?t=23829
In a template
you can either access the id
from route props or id
from the segment.
// app/[id]/template.tsx
export default function Template({ children }: { children: React.ReactNode }) {
const router = useRouter();
const { id } = router.query; // Access the dynamic route parameter
return (
<>
<h1>{id}</h1>
<div>{children}</div>
</>
)
}
or
const segment = useSelectedLayoutSegment(); // This will give the `[id]` segment directly
Caveat: this will turn the component into a Client Component, which means it no longer renders on the server. It's probably not what you want.
You can also use the layout to pass the param to the template.
This way both the Layout and the Template will remain as Server Components.
// app/[id]/layout.tsx
import Template from './template';
export default function Layout({ children, params }) {
const { id } = params; // Extract the route parameter `id`
return (
<Template id={id}>
{children}
</Template>
);
}
Caveat: As the whole point of layouts and templates is to share common UI across pages, at this point you probably want to use a
Page
or ideally one of its subcomponents instead. See template docs and a detailed explanation in Option 1 above.
Upvotes: 0