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.tsxand createroutes/_regular/index.tsx.__root.tsxto_regular/route.tsx.Note that
routes/index.tsxandroutes/_regular/index.tsxcannot 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.jsxis still a crucial file. It’s required by the@tanstack/router-plugin/viteplugin to generate therouteTree.gen.tsfile, which is then imported and initialized in the app’s entry point (/src/main.jsxin 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.jsxThank 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 :