Dynamic breadcrumbs & RootLayout, how should I combine those two?
Answered
TheMelonAssassin posted this in #help-forum
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body
className={cn(
"min-h-screen bg-background font-sans antialiased",
fontSans.variable
)}
>
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
<BreadcrumbItem className="hidden md:block">
<BreadcrumbLink href="#">
Building Your Application
</BreadcrumbLink>
</BreadcrumbItem>
<BreadcrumbSeparator className="hidden md:block" />
<BreadcrumbItem>
<BreadcrumbPage>Data Fetching</BreadcrumbPage>
</BreadcrumbItem>
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
<main>{children}</main>
</SidebarInset>
</SidebarProvider>
</body>
</html>
);
}
This is my
RootLayout
. As you can see I've added a sidebar from shadcn to it so that it can be used across all pages. I also like the breadcrumbs, but I'm struggling to understand on how I can make them more dynamic.
Say I navigated to
/events
import { getAllEvents } from "@/actions/events";
import { columns } from "@/components/events/columns";
import { EventTable } from "@/components/events/event-table";
export default async function Page() {
const events = await getAllEvents();
return <EventTable columns={columns} data={events} />;
}
I'd like to define the breadcrumbs here and then display them in the rootlayout
Answered by TheMelonAssassin
Alright I got there, just had to switch around some stuff
layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body
className={cn(
"min-h-screen bg-background font-sans antialiased",
fontSans.variable
)}
>
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<main>{children}</main>
</SidebarInset>
</SidebarProvider>
</body>
</html>
);
}
breadcrumb-wrapper.tsx
export type BreadcrumbWrapperProps = {
breadcrumbs: {
label: string;
href?: string; // Optional for the current page
}[];
};
export function BreadCrumbWrapper({ breadcrumbs }: BreadcrumbWrapperProps) {
return (
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
{breadcrumbs.map(({ label, href }, index) => (
<>
<BreadcrumbItem key={label}>
{href ? (
<BreadcrumbLink href={href}>{label}</BreadcrumbLink>
) : (
<BreadcrumbPage>{label}</BreadcrumbPage>
)}
</BreadcrumbItem>
{index < breadcrumbs.length - 1 && (
<BreadcrumbSeparator className="hidden md:block" />
)}
</>
))}
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
);
}
page.tsx
const breadcrumbs = [{ label: "Dashboard", href: "/" }, { label: "Events" }];
export default async function Page() {
const events = await getAllEvents();
return (
<>
<BreadCrumbWrapper breadcrumbs={breadcrumbs} />
<div className="mx-4 mb-4 rounded-3xl bg-white p-10 shadow-lg">
<EventTable columns={columns} data={events} />
</div>
</>
);
}
7 Replies
Antillean Palm Swift
I would suggest making BreadCrumbs a separate component and use ‘usePathname’ - https://nextjs.org/docs/app/api-reference/functions/use-pathname
and get the pathname and show it in the breadcrumbs.
and get the pathname and show it in the breadcrumbs.
@Antillean Palm Swift I would suggest making BreadCrumbs a separate component and use ‘usePathname’ - https://nextjs.org/docs/app/api-reference/functions/use-pathname
and get the pathname and show it in the breadcrumbs.
Yeah I checked this out, but for instance when I'm at URL
I know how to do all that with a wrapper and passing some values in after fetching the data from the db.
It's moreso how can I keep the wrapper in the rootlayout and have full control over what I pass into it
events/:id
I'd rather not show the id but the name of the event.I know how to do all that with a wrapper and passing some values in after fetching the data from the db.
It's moreso how can I keep the wrapper in the rootlayout and have full control over what I pass into it
@TheMelonAssassin Yeah I checked this out, but for instance when I'm at URL `events/:id` I'd rather not show the id but the name of the event.
I know how to do all that with a wrapper and passing some values in after fetching the data from the db.
It's moreso how can I keep the wrapper in the rootlayout and have full control over what I pass into it
It's better to have BreadCumbs component as a client component
@James4u It's better to have BreadCumbs component as a client component
I copied this setup straight from shad/cn blocks, I'm messing around with NextJS for the first time so quite a way to go
It's the combination with the
<SidebarProvider>
, <AppSidebar />
, <SidebarInset>
, <SidebarTrigger>
that's giving me issues.Let me rework the layout
Alright I got there, just had to switch around some stuff
layout.tsx
export default function RootLayout({
children,
}: {
children: React.ReactNode;
}) {
return (
<html lang="en">
<body
className={cn(
"min-h-screen bg-background font-sans antialiased",
fontSans.variable
)}
>
<SidebarProvider>
<AppSidebar />
<SidebarInset>
<main>{children}</main>
</SidebarInset>
</SidebarProvider>
</body>
</html>
);
}
breadcrumb-wrapper.tsx
export type BreadcrumbWrapperProps = {
breadcrumbs: {
label: string;
href?: string; // Optional for the current page
}[];
};
export function BreadCrumbWrapper({ breadcrumbs }: BreadcrumbWrapperProps) {
return (
<header className="flex h-16 shrink-0 items-center gap-2 transition-[width,height] ease-linear group-has-[[data-collapsible=icon]]/sidebar-wrapper:h-12">
<div className="flex items-center gap-2 px-4">
<SidebarTrigger className="-ml-1" />
<Separator orientation="vertical" className="mr-2 h-4" />
<Breadcrumb>
<BreadcrumbList>
{breadcrumbs.map(({ label, href }, index) => (
<>
<BreadcrumbItem key={label}>
{href ? (
<BreadcrumbLink href={href}>{label}</BreadcrumbLink>
) : (
<BreadcrumbPage>{label}</BreadcrumbPage>
)}
</BreadcrumbItem>
{index < breadcrumbs.length - 1 && (
<BreadcrumbSeparator className="hidden md:block" />
)}
</>
))}
</BreadcrumbList>
</Breadcrumb>
</div>
</header>
);
}
page.tsx
const breadcrumbs = [{ label: "Dashboard", href: "/" }, { label: "Events" }];
export default async function Page() {
const events = await getAllEvents();
return (
<>
<BreadCrumbWrapper breadcrumbs={breadcrumbs} />
<div className="mx-4 mb-4 rounded-3xl bg-white p-10 shadow-lg">
<EventTable columns={columns} data={events} />
</div>
</>
);
}
Answer