This episode builds a route tree with TanStack Router: createRootRoute and createRoute, nested routes with layouts, data loading through loaders, and navigation with Link where the URL is truly connected to application state.

Query and Table manage data within a single page. But real apps have many pages: dashboards, user lists, project details. That's where TanStack Router comes in — a type-safe router that organizes the entire app as a route tree and loads data before components render.
Episode 7 builds the first route tree, nested routes with layouts, data loading through loaders, and navigation with Link and route state. This is the routing foundation we'll enrich in episode 10.
By the end of the episode, you'll have a multi-page app where the URL, data, and components are kept in sync safely by types.
The entire app starts from the root route. Child routes are defined with createRoute and combined through addChildren:
import { createRootRoute, createRoute, createRouter, RouterProvider } from "@tanstack/react-router"
const rootRoute = createRootRoute({
component: () => <h1>Halo TanStack Router</h1>,
})
const indexRoute = createRoute({
getParentRoute: () => rootRoute,
path: "/",
component: () => <p>Halaman utama</p>,
})
const routeTree = rootRoute.addChildren([indexRoute])
const router = createRouter({ routeTree })getParentRoute: () => rootRoute places indexRoute as a child of the root. createRouter({ routeTree }) produces a ready-to-use router, then rendered through <RouterProvider router={router} />.
Nested routes enable shared layouts. The parent route renders <Outlet />, and child routes render in that position:
import { Outlet, Link } from "@tanstack/react-router"
const rootRoute = createRootRoute({
component: () => (
<>
<nav>
<Link to="/">Beranda</Link>
<Link to="/pengguna">Pengguna</Link>
</nav>
<Outlet />
</>
),
})
const penggunaRoute = createRoute({
getParentRoute: () => rootRoute,
path: "pengguna",
component: PenggunaPage,
})<Outlet /> is where child routes render, so the nav and header layout appears on every page without duplication. <Link to="/pengguna"> is TanStack Router's navigation component, which automatically handles active state and URL type-checking.
Loaders run before the route component renders. Components read the result through useLoaderData:
import { useLoaderData } from "@tanstack/react-router"
const postRoute = createRoute({
getParentRoute: () => rootRoute,
path: "posts/$postId",
loader: ({ params }) => ambilPosting(params.postId),
component: () => {
const data = useLoaderData({ from: postRoute.id })
return <article>{data.judul}</article>
},
})path: "posts/$postId" marks a dynamic segment; its actual value is in params.postId when the loader is called. useLoaderData({ from: postRoute.id }) retrieves the loader result with a type inferred from the route — a type-safety guarantee other routers don't have.
TanStack Router shows a pending state while the loader runs and handles errors separately:
const postRoute = createRoute({
getParentRoute: () => rootRoute,
path: "posts/$postId",
loader: ({ params }) => ambilPosting(params.postId),
pendingComponent: () => <p>Memuat posting...</p>,
errorComponent: ({ error }) => <p>Gagal: {error.message}</p>,
component: PostComponent,
})pendingComponent shows while the loader hasn't finished, and errorComponent shows when the loader throws. Both make navigation transitions feel responsive without manual loading logic on every page.
Tip
File-based routing with createFileRoute and the @tanstack/router-plugin automatically builds the route tree from the folder structure. Episode 10 uses this approach for a larger app.
Episode 7 wrapped up basic routing: a route tree with createRootRoute and createRoute, nested routes with Outlet for shared layouts, loaders for loading data before render, and pending and error components for navigation transitions.
Key takeaways:
In the next episode, episode 8, we'll discuss advanced query patterns — prefetching and query cancellation, dependent queries and parallel queries, query functions with modern fetchers, and syncing server state between browser tabs. Back to TanStack Query, but at a much deeper level!