if you read this issue discussion on GitHub :
Custom Layout for Specific Routes in tanstack/router #1102
you’ll see that this is a big issue. Surprisingly, such a basic feature is not available in a large project like TanStack Router. Even though the issue is marked as resolved, that’s not really the case. I tried all the suggested solutions, but none of them worked for me. I also went through the documentation and found that there’s still no official support for this. However, I managed to solve it with a workaround.
![]() |
![]() |
![]() |
---|
The Scenario :
I am working on a [react + vite + Tanstack Router] Project for a clothes store,
I have two type of pages :
- regular pages : Home Page, Product Page, Collection Page, Search Page, Checkout Page.
- Dashboard Pages : Statistics Page, CRUD Product Page, Handling Orders Page,
The regular pages anyone can navigate them, and they have layout of <NavBar />
& <Footer />
The Dashboard pages for the admin, and they have layout of <SideBar />
The routing type implemented here is the File-Based Routing
.
The Problem :
when I set a custom layout for the dashboard, it wrapped by the layout of the regular pages.
The /src/routes
directory Hierarchy :
\routes
| checkout.jsx
| collection.jsx
| index.jsx
| product.jsx
| search.jsx
| __root.jsx
|
\---dashboard
| index.jsx
| route.jsx
|
\---products
index.jsx
the src/routes/__root.jsx
file :
import { createRootRoute, Outlet } from "@tanstack/react-router";
import Navbar from "@/components/Navbar";
import Footer from "@/components/Footer";
import NotFoundPage from "@/components/NotFoundPage";
export const Route = createRootRoute({
notFoundComponent: () => <NotFoundPage />,
component: () => {
return (
<div>
<Navbar />
<Outlet />
<Footer />
</div>
);
},
});
the /src/routes/dashboard/route.jsx
file :
import { Outlet, Link, createFileRoute } from "@tanstack/react-router";
import SideBar from "@/componenets/SideBar";
export const Route = createFileRoute("/dashboard")({
component: () => {
return (
<div className="flex h-screen bg-gray-50">
<SideBar />
<main className="flex-1 p-6 overflow-y-auto">
<Outlet />
</main>
</div>
);
},
});
so when i browse /Dashboard/products
The Given Result :
<NavBar />
<div className="flex h-screen bg-gray-50">
<SideBar />
<main className="flex-1 p-6 overflow-y-auto">
<Outlet />
</main>
</div>
<Footer/>
The Wanted Result :
<div className="flex h-screen bg-gray-50">
<SideBar />
<main className="flex-1 p-6 overflow-y-auto">
<Outlet />
</main>
</div>
So How can you set a custom Layout for Dashboard Sub Pages.
The Solution :
The trick is to use conditional rendering: if the route starts with /dashboard/, it won’t be wrapped by any component.
the src/routes/__root.jsx
file again, but with Conditional Rendering :
import React, { useState} from "react";
import { createRootRoute, Outlet, useLocation } from "@tanstack/react-router";
import Navbar from "../components/Navbar";
import Footer from "../components/Footer";
import NotFoundPage from "../components/NotFoundPage";
export const Route = createRootRoute({
notFoundComponent: () => <NotFoundPage />,
component: RootComponent,
});
function RootComponent() {
const location = useLocation();
const pathname = location.pathname;
if (pathname.startsWith("/dashboard")) {
return <DashboardRoute />;
} else {
return <RegularRoute />;
}
}
function DashboardRoute() {
return (
<>
<Outlet />
</>
);
}
function RegularRoute() {
return (
<Navbar />
<Outlet />
<Footer />
);
}
I hope this is helpful to everyone.
leave a love & a comment so more people can reach it.
Top comments (6)
Here is a complete solution that achieves multiple root routes while leveraging the efficient partial route rendering.
This solution uses pathless routes for both route groups.
These are the keys.
routes/index.tsx
and createroutes/_regular/index.tsx
.__root.tsx
to_regular/route.tsx
.Note that
routes/index.tsx
androutes/_regular/index.tsx
cannot exist at the same time.I saw like this solution in the issue discussion before, it didn't work to me, or may be I didn't understand it well, it was confusing.
and about pathless layout, I read about it before in the official documentation and it didn't work also.
Anyway !
When I looked at your solution, I realized I had been overcomplicating things. I tried your approach in my project, and it worked well—at least until I ran into the following error:
The issue was that
__root.jsx
is still a crucial file. It’s required by the@tanstack/router-plugin/vite
plugin to generate therouteTree.gen.ts
file, which is then imported and initialized in the app’s entry point (/src/main.jsx
in my case).Here’s my
/src/main.jsx
:/src/main.jsx
:So, your solution works perfectly, but it just needs this small additional file:
/src/routes/__root.jsx
Thank you so much, I appreciate your help.
Yeah I forgot the __root.tsx sorry. It is required.
Thanks for sharing the idea.
However, the problem is, since the root component is using the hook
useLocation()
, whenever the route changes, the whole application is re-rendered.Your solution follows the official guidelines and represents best practices. In contrast, my approach was more of a workaround. Your implementation is clean and maintainable, whereas mine turned into spaghetti code. Clearly, your approach is the better one.
@jhaemin
may be the cover image should be like this :