Recommended resources
The freely available resources this lesson will direct you to.
Hello! Welcome back to our course on migrating React SPAs to SSR with Tanstack.
In our last lesson, we explored how Tanstack Router provides comprehensive type safety for navigation, path parameters, and search parameters. We saw how defining validation schemas at the route level allows us to treat URL state with the same reliability as our internal application state.
Today, we'll focus on a cornerstone of building scalable web applications: structuring applications with nested routes and shared layouts. This is how you create persistent UI elements like navigation bars, sidebars, and footers that don't re-render on every page change. For anyone who has managed complex application states and UI consistency, you'll recognize how a router-level solution for layouts simplifies development.
We will cover:
- The fundamental
__root.tsxlayout and the<Outlet />component. - Creating nested routes using both directory and file-based conventions.
- Applying layouts to specific sections of your application.
- Advanced organizational patterns like pathless layouts and route groups.
1. The Foundation: Root Layout and the <Outlet />
Every Tanstack Router application starts with a single root layout. This is a special route that acts as the top-level container for your entire application. It's the perfect place for elements that should be visible on every page, such as a main navigation bar or a footer.
The key to making this work is the <Outlet /> component. Think of it as a placeholder or a "slot" where the content of the currently active child route will be rendered.
Let's watch a video that demonstrates this core concept.
Tanstack Router Tutorial - Routing, Lazy Loading, Data Fetching, Params...
This clip from PedroTech's "Tanstack Router Tutorial" clearly illustrates how to set up the root layout file and explains the role of the <Outlet /> component for rendering child routes.
Watch the folder structure. Pay attention to how the _root.tsx file is structured with a navbar, a footer, and the <Outlet /> component in between.
As you saw, the structure is straightforward. You create a file named __root.tsx (or _root.tsx) inside your src/routes/ directory.
Here's a typical example:
// src/routes/__root.tsx
import { createRootRoute, Link, Outlet } from '@tanstack/react-router'
import { TanStackRouterDevtools } from '@tanstack/router-devtools'
export const Route = createRootRoute({
component: () => (
<>
<header className="p-4 flex gap-4 bg-gray-100">
<Link to="/" className="[&.active]:font-bold">
Home
</Link>{' '}
<Link to="/posts" className="[&.active]:font-bold">
Posts
</Link>
</header>
<hr />
<main className="p-4">
{/* Child routes will be rendered here */}
<Outlet />
</main>
<footer className="p-4 text-center text-sm text-gray-500">
Copyright 2024
</footer>
<TanStackRouterDevtools />
</>
),
})
With this file in place, the header and footer will wrap every page in your application. The content of each specific page will be injected where <Outlet /> is placed.
2. Creating and Nesting Routes
Now that we have our main layout, let's add some pages. In Tanstack Router's file-based system, the file and folder structure inside src/routes/ directly maps to the URL structure. Any route you create will automatically render inside the <Outlet /> of its parent route.
There are two primary ways to create nested routes. Let's say we want a /posts page and a /posts/123 page.
-
Directory-based Nesting (Recommended for clarity):
/posts:src/routes/posts/index.tsx/posts/$postId:src/routes/posts/$postId.tsx
-
File-based (Dot Notation) Nesting:
/posts:src/routes/posts.tsx(orposts.index.tsx)/posts/$postId:src/routes/posts.$postId.tsx
Both approaches result in the same URL structure and behavior. The directory-based approach is often preferred as it keeps related files neatly organized in their own folder.
The following video segments demonstrate how simple page routes and then a nested /posts route are created and how they fit into the root layout.
Tanstack Router Tutorial - Routing, Lazy Loading, Data Fetching, Params...
Continuing with the PedroTech tutorial, these clips show the creation of simple index and about pages, and then a nested 'posts' route using a folder structure.
First, watch basic route setup. This covers creating basic routes and integrating the router in main.tsx to see the layout in action. Then, watch dynamic nested routes, which specifically demonstrates creating a nested /posts route with a dynamic $id child route.
3. Layouts at Any Level
Layouts are not limited to the root of your application. Any route can act as a layout for its own children. This is a powerful pattern for creating section-specific layouts, like a dashboard with a unique sidebar.
To make a route a layout, you simply include an <Outlet /> in its component. Its child routes will then render inside that outlet.
Let's imagine a /dashboard section with its own navigation.
File Structure:
src/routes/
├── dashboard/
│ ├── route.tsx # <-- Layout for /dashboard/* routes
│ ├── index.tsx # <-- Content for /dashboard
│ └── settings.tsx # <-- Content for /dashboard/settings
└── __root.tsx
The layout file dashboard/route.tsx would define the shared UI for the dashboard section:
// src/routes/dashboard/route.tsx
import { createFileRoute, Link, Outlet } from '@tanstack/react-router'
export const Route = createFileRoute('/dashboard')({
component: DashboardLayout,
})
function DashboardLayout() {
return (
<div className="flex">
<aside className="w-1/4 bg-gray-200 p-4">
<nav className="flex flex-col gap-2">
<Link to="/dashboard" className="[&.active]:font-bold">
Overview
</Link>
<Link to="/dashboard/settings" className="[&.active]:font-bold">
Settings
</Link>
</nav>
</aside>
<div className="w-3/4 p-4">
{/* Dashboard child routes render here */}
<Outlet />
</div>
</div>
)
}
Now, when a user navigates to /dashboard/settings, the Settings component will render inside the DashboardLayout's <Outlet />, which itself is rendered inside the RootLayout's <Outlet />.
To solidify this concept, please read the official documentation on Layout Routes.
Routing Concepts | TanStack Router React Docs
The official TanStack Router documentation provides a concise explanation of Layout Routes, showing how they wrap child routes.
Read the section titled 'Layout Routes'. Note the two examples showing how both file-based dot notation and directory structures can create layouts.
4. Advanced Organizational Patterns
For large applications, keeping the routes directory organized is critical. Tanstack Router provides two powerful conventions for grouping routes without affecting the URL paths.
Pathless Layout Routes (_)
Sometimes you want to group routes under a shared layout, but you don't want that group to add a segment to the URL. A common use case is authentication: you might have /login, /register, and /forgot-password routes that all share a similar centered layout and logic, but you don't want them to live under a common URL prefix like /auth/login.
You can achieve this by prefixing the layout file with an underscore (_).
src/routes/_auth.tsx: A layout that doesn't create a URL segment.src/routes/_auth.login.tsx: Renders at/loginbut uses the_auth.tsxlayout.src/routes/_auth.register.tsx: Renders at/registerbut also uses the_auth.tsxlayout.
Pathless Route Group Directories (())
This is a purely organizational tool. If you want to group related route files together in the filesystem without affecting their URLs or layouts, you can place them in a directory whose name is wrapped in parentheses.
src/routes/(marketing)/about.tsx->/aboutsrc/routes/(marketing)/contact.tsx->/contact
This has no effect on the final routes; it's just a way to prevent your routes directory from becoming a flat, unmanageable list of files.
Routing Concepts | TanStack Router React Docs
The official documentation explains these two powerful organizational patterns. Understanding them is key to maintaining a clean route structure in a large project.
Read the sections 'Pathless Layout Routes' and 'Pathless Route Group Directories'. Pay close attention to the file structure examples and the resulting URL paths.
Summary & Example Structure
Let's synthesize these concepts into a hypothetical structure for a moderately complex application.
src/routes/
├── __root.tsx # App shell: Navbar, Footer. Renders <Outlet />.
│
├── index.tsx # Homepage, renders at `/`
│
├── (marketing)/ # Organizational group, no URL impact.
│ ├── about.tsx # Renders at `/about`
│ └── contact.tsx # Renders at `/contact`
│
├── _authenticated.tsx # Pathless layout for protected routes.
│ # Checks for user session, provides user context.
│ # Renders its own <Outlet />.
│
├── _authenticated.dashboard/ # Nested route using the authenticated layout.
│ ├── route.tsx # Layout for `/dashboard/*`: a dashboard-specific sidebar.
│ │ # Renders its own <Outlet />.
│ ├── index.tsx # Renders at `/dashboard`
│ └── settings.tsx # Renders at `/dashboard/settings`
│
└── posts/ # A standard nested route.
├── index.tsx # Renders at `/posts`
└── $postId.tsx # Renders at `/posts/some-id`
This structure effectively uses all the patterns we've discussed to create a scalable and maintainable routing hierarchy.
Conclusion
In this lesson, we've moved from defining individual pages to architecting the very structure of our application. By mastering nested routes and layouts, you can build complex UIs that are both scalable and easy to reason about.
Key Takeaways:
- Root Layout (
__root.tsx): The top-level wrapper for your entire application, containing global UI and an<Outlet />. - Nested Routes: Created by organizing files in directories or with dot notation, allowing child routes to render inside parent layouts.
- Layouts at Any Level: Any route can serve as a layout for its children by including an
<Outlet />, enabling section-specific UI. - Organizational Patterns: Use pathless layouts (
_) to group routes under a shared layout without affecting the URL, and use group directories (()) for pure filesystem organization.
In our next lesson, we will dive into one of the most powerful features for SSR: "Use route loaders to fetch data on the server before component rendering." We'll see how layouts and loaders work together to fetch data efficiently for entire sections of your app.