Public and Private Layout
Table of Contents
Introduction
TanStack Router is a powerful routing solution for React applications. This guide focuses on creating a structured router with:
- Public layouts for unauthenticated users (login, register, landing pages)
- Private layouts for authenticated users (dashboard, profile, settings)
Project Structure
Recommended project structure for routes:
src/
├── routes/
│ ├── root.tsx # Root route
│ ├── public.tsx # Public routes
│ ├── private.tsx # Private routes
│ └── index.ts # Main router export
├── shared/
│ └── layout/
│ ├── PrivateLayout.tsx # Layout for authenticated pages
│ └── PublicLayout.tsx # Layout for unauthenticated pages
└── pages/
├── Home.tsx # Public page
├── Login.tsx # Public page
├── Register.tsx # Public page
├── Dashboard.tsx # Private page
└── Profile.tsx # Private page
Setup Steps
Step 1: Root Route
Create the root route that will serve as the parent for all other routes.
// src/routes/root.tsx
import { createRootRouteWithContext, Outlet } from "@tanstack/react-router";
import { TanStackRouterDevtools } from "@tanstack/react-router-devtools";
export const rootRoute = createRootRouteWithContext<{
// Add your context types here if needed
// Example: auth?: { user: User }
}>()({
component: () => (
<>
<Outlet />
{process.env.NODE_ENV === "development" && <TanStackRouterDevtools />}
</>
),
});
Step 2: Layout Components
Create layout components for public and private sections of your app.
Private Layout
// src/shared/layout/PrivateLayout.tsx
import { Outlet } from "@tanstack/react-router";
import Navbar from "@/components/Navbar"; // Your navigation component
import Sidebar from "@/components/Sidebar"; // Your sidebar component
export default function PrivateLayout() {
return (
<div className="min-h-screen bg-gradient-to-br from-gradient-start to-gradient-end dark:from-gradient-start dark:to-gradient-end">
<Navbar />
<div className="flex">
<Sidebar />
<main className="flex-1 p-6">
<Outlet />
</main>
</div>
</div>
);
}
Public Layout
// src/shared/layout/PublicLayout.tsx
import { Outlet } from "@tanstack/react-router";
import PublicHeader from "@/components/PublicHeader"; // Your public header component
import Footer from "@/components/Footer"; // Your footer component
export default function PublicLayout() {
return (
<div className="min-h-screen bg-gradient-to-br from-gradient-start to-gradient-end dark:from-gradient-start dark:to-gradient-end">
<PublicHeader />
<div className="flex items-center justify-center min-h-screen p-4">
<Outlet />
</div>
<Footer />
</div>
);
}
Step 3: Public Routes
Create the public routes file:
// src/routes/public.tsx
import { createRoute, lazyRouteComponent } from "@tanstack/react-router";
import { rootRoute } from "./root";
// Create the public layout route
export const publicRootRoute = createRoute({
getParentRoute: () => rootRoute,
id: "public-layout",
component: lazyRouteComponent(() => import("@/shared/layout/PublicLayout")),
});
// Define public routes
export const homeRoute = createRoute({
getParentRoute: () => publicRootRoute,
path: "/",
component: lazyRouteComponent(() => import("@/pages/Home")),
});
export const loginRoute = createRoute({
getParentRoute: () => publicRootRoute,
path: "login",
component: lazyRouteComponent(() => import("@/pages/Login")),
});
export const registerRoute = createRoute({
getParentRoute: () => publicRootRoute,
path: "register",
component: lazyRouteComponent(() => import("@/pages/Register")),
});
// Add more public routes as needed
// Group all public routes
export const publicRoute = publicRootRoute.addChildren([
homeRoute,
loginRoute,
registerRoute,
]);
Step 4: Private Routes
Create the private routes file:
// src/routes/private.tsx
import {
createRoute,
lazyRouteComponent,
redirect,
} from "@tanstack/react-router";
import { rootRoute } from "./root";
// Create the private layout route
export const privateRootRoute = createRoute({
getParentRoute: () => rootRoute,
id: "private-layout",
component: lazyRouteComponent(() => import("@/shared/layout/PrivateLayout")),
// Optional: Add authentication check
beforeLoad: async ({ context }) => {
// If using authentication context
if (!context.auth?.isAuthenticated) {
throw redirect({
to: "/login",
search: {
redirect: location.pathname,
},
});
}
},
});
// Define private routes
export const dashboardRoute = createRoute({
getParentRoute: () => privateRootRoute,
path: "dashboard",
component: lazyRouteComponent(() => import("@/pages/Dashboard")),
});
export const profileRoute = createRoute({
getParentRoute: () => privateRootRoute,
path: "profile",
component: lazyRouteComponent(() => import("@/pages/Profile")),
});
export const settingsRoute = createRoute({
getParentRoute: () => privateRootRoute,
path: "settings",
component: lazyRouteComponent(() => import("@/pages/Settings")),
});
// Add more private routes as needed
// Group all private routes
export const privateRoute = privateRootRoute.addChildren([
dashboardRoute,
profileRoute,
settingsRoute,
]);
Step 5: Combine Routes
Create the main router file:
// src/routes/index.ts
import { createRouter } from "@tanstack/react-router";
import { rootRoute } from "./root";
import { publicRoute } from "./public";
import { privateRoute } from "./private";
// Create the route tree
const routeTree = rootRoute.addChildren([publicRoute, privateRoute]);
// Create and export the router
export const router = createRouter({
routeTree,
// Optional: Add context
context: {
// Example: auth: undefined,
},
});
// Register router history
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
Step 6: Router Configuration
Setup the router in your main application file:
// src/App.tsx
import { ThemeProvider } from "@/shared/component/theme/ThemeProvider";
import { RouterProvider } from "@tanstack/react-router";
import { router } from "@/routes/index";
declare module "@tanstack/react-router" {
interface Register {
router: typeof router;
}
}
export default function App() {
return (
<ThemeProvider>
<RouterProvider router={router} />
</ThemeProvider>
);
}