How to use slots in NextJS
Slots are a key feature on NextJS when we are building a web application. The main goal of this feature is to dynamically or conditionally, render a specific page or another (in simple terms). An example of slot usage is for example: a Dashboard. An application have different users with different roles in it, for example: admin, moderator, client, user, etc. Without slots, you need to create a dashboard for each role and those dashboards must need to be differenciated by the URL (since in NextJS, a folder with a page.tsx in it, is a route of your app). In this case, is necessary to have some "frame" or "model" to only replace the parts that we need to replace. A Dashboard have common components inside, such as the sidebar, cards, common pages for all the roles (for example profile page or settings), etc... Here is where slots came into action.
In my latest project you can visit: https://www.proautonomos.com, I use slots to conditionally render the dashboard, one for the professional and another for the clients.
Project Stucture
The folder named @professionals
and @client
are slots declaration in NextJS. Now I show you how it looks like one of the slots inside.
Client Folder Structure
As you see, is not different at we have in a basic NextJS project, the only difference between this client folder and a brand new NextJS project is, instead of calling the main folder container "app" is called "@client". Keep that in mind, its going to be important in the future.
Now lets leave a moment the slot concept and lets talk about "children".
children - the main slot
Children is one of the most important part on React, we use it a lot in different parts on different ways. When you have a component that you want to compose or even if we want to render a page.
children
is the main slot in React, being replaced by the components you want to render.
Here is an example of what I want to tell:
import type React from "react"; import type { Metadata } from "next"; import { Inter } from "next/font/google"; import "./globals.css"; import { cn } from "@/lib/utils"; import { Analytics } from "@vercel/analytics/react"; import { SpeedInsights } from "@vercel/speed-insights/next"; const inter = Inter({ subsets: ["latin"] }); export const metadata: Metadata = { title: "ProAutonomos - Encuentra servicios profesionales", description: "Encuentra electricistas, programadores, plomeros, diseñadores y más.", }; export default function RootLayout({ children, }: Readonly<{ children: React.ReactNode; }>) { return ( <html lang="es"> <body className={cn(`${inter.className}`)}> <Analytics /> <SpeedInsights /> {children} </body> </html> ); }
This is a layout structure, the layout structure in NextJS could be a client component or a server component but in any case, is a must to add the "children" props as params in the RootLayout, since the layout function likes a "template" for the page file.
The content you put inside the page.tsx
, is going to be inside the "children" prop (between <SpeedInsights />
and </body>
tags in my example).
For slots, is exactly the same. when we create a slot in NextJS, we are creating a sort of "special children" you can render inside the layout. Here is the example of how I am using slots for ProAutonomos:
import type React from "react"; import { getServerSession } from "@/hooks/server/get-server-session"; import { redirect } from "next/navigation"; import { SidebarInset, SidebarProvider, SidebarTrigger, } from "@/components/ui/sidebar"; import { AppSidebar } from "@/components/layout/sidebar/app-sidebar"; import { getUserInfo } from "@/actions/general/get-user-info"; interface DashboardLayoutProps { children: React.ReactNode; professionals: React.ReactNode; client: React.ReactNode; } const DashboardLayout = async ({ children, professionals, client, }: DashboardLayoutProps) => { const session = await getServerSession(); if (!session) { redirect("/auth/login"); } const isProfessional = session.user?.role === "professional"; const isClient = session.user?.role === "client"; if (!isProfessional && !isClient) { return ( <div className="flex w-full min-h-screen bg-muted/30"> {/* Guest Main Content */} <main className="w-full"> <div>{children}</div> </main> </div> ); } const user = await getUserInfo(session.user?.id); if (!user || !user.user) { return null; } return ( <SidebarProvider> <AppSidebar user={user?.user} /> <SidebarInset> {/* Main Content */} <div className="p-4"> <SidebarTrigger className="bg-accent size-10" /> </div> <main className="w-full p-6"> {isProfessional ? professionals : client} </main> </SidebarInset> </SidebarProvider> ); }; export default DashboardLayout;
As you see in the DashboardLayout
, I have 2 different options to render in the same dashboard route, based on "isProfessional" (between the <main />
tags).
But what happend if the user is not a professional or a client? what I need to render?
For those specific scenarios, NextJS offers another file convention called "default.tsx
".
The default.tsx
is a special file, required when you are working with slots, due to the necessity of having a place to be when the conditions of the slots are not met or if we want to render a different thing while you are preparing everything in the main slot page.
A bit confused? dont worry, the concept here is this: When you are working with slots in NextJS, you need to create the slot folder where you gonna put your pages and a default page where NextJS can render the children, since children is a core feature of React and NextJS, you cannot avoid it the use of children, as a fallback for unmatching slot routes. For example: if I want to access to "/dashboard/page-exist-on-client-slot" its going to enter into the "@client" slot while if I try to access to "/dashboard/this-page-dont-exist-in-any-slot" its going to be inside the fallback (default.tsx
).
Hope you have a bit of more clarity on how to use NextJS Slots, an important feature that this framework offers to us as a developers to create mantainable and complex projects.
Here I leave the documentation of this topic for more detailed explanation and technical: NextJS Documentation: Slots