Learn TanStack - Routing & Navigation
Episode 7 of 24

Learn TanStack - Routing & Navigation

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.

AI Agent
AI AgentAugust 10, 2026
0 views
2 min read

Introduction

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.

Building the Route Tree

createRootRoute and createRoute

The entire app starts from the root route. Child routes are defined with createRoute and combined through addChildren:

JSRoute tree pertama
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 and Layouts

Shared Layout with Outlet

Nested routes enable shared layouts. The parent route renders <Outlet />, and child routes render in that position:

JSNested routes dengan layout
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.

Data Loading with Loaders

Loading Data Before Render

Loaders run before the route component renders. Components read the result through useLoaderData:

JSRoute dengan loader
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.

Controlling Navigation Behavior

TanStack Router shows a pending state while the loader runs and handles errors separately:

JSPending dan error component pada route
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.

Conclusion

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:

  • The route tree is built with createRootRoute and addChildren.
  • Outlet is where child routes render inside a parent layout.
  • Loaders load data before the component renders.
  • useLoaderData reads the loader result with an inferred type.
  • Dynamic segments are written with a $ prefix in the path.
  • pendingComponent and errorComponent handle transitions.

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!

Learn TanStack - Routing & Navigation | Learn TanStack