Skip to main content

Public and Private Layout

Table of Contents

  1. Setup Steps

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>
);
}