AV
HomeAboutProjectBlog

© 2026 Ave syah Shina. All rights reserved.

  1. Home
  2. Blog
  3. 07 — Routing in React — URLs as State, React Router and TanStack Router

07 — Routing in React — URLs as State, React Router and TanStack Router

August 13, 20265 min read
Download as Markdown

"Just navigating between pages" was my routing model, and it made routers look like page-switching utilities. The framing that straightened it out: in a single-page application, the URL is just another piece of state, and a router is the library that maps URL state to which components render. [1] Once I saw the URL as state, the rest followed — links are state setters, the back button is an undo, and route params are state that flows into components.

Why a single-page app needs a router at all

A traditional multi-page site navigates by full document reloads — each link fetches a fresh HTML page from the server. A React SPA loads once and then rewrites parts of the page in place, so "navigation" can't rely on the browser's default reload behavior. A client-side router fills that gap by:

  • intercepting link clicks and pushState calls so they update the URL without a full reload,
  • reading the current URL and deciding which route component to mount,
  • syncing with the browser's back/forward buttons so history still works.

The payoff is the SPA's signature: instant page transitions, no white flash, persistent UI state across "pages." The cost is that I now own the URL-to-component mapping, which is what a router library manages for me.

React Router: the default

React Router is the standard library for this [1][2]. It provides a declarative way to define routes — each route maps a URL pattern to a component — and handles the link interception, history, and nested routing. The core pieces:

  • <BrowserRouter> wraps the app and enables client-side routing.
  • <Routes> and <Route> declare the URL → component mapping, including dynamic segments like :id.
  • <Link> renders an anchor that changes the URL without a reload.
  • useParams reads dynamic segments; useNavigate pushes programmatically.
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/users/:id" element={<UserProfile />} />
<Route path="*" element={<NotFound />} />
</Routes>
</BrowserRouter>

Routes nest, so a /dashboard/settings route can render <Dashboard> with an <Outlet> for its child routes. That nesting model is the part I underused at first — it's how shared layouts (a sidebar that stays put while the inner panel changes) are built.

TanStack Router: the type-safe challenger

TanStack Router is a newer, more ambitious router [3]. Its distinguishing trait is full type safety end to end — route params, search/query parameters, and links are all typed, so a typo in a path or a wrong param type is a compile error rather than a runtime 404. It also treats search params as first-class typed state, with built-in validation.

// a route definition infers its params and search schema
export const Route = createFileRoute('/users/$userId')({
validateSearch: (search) => ({ tab: search.tab ?? 'overview' }),
component: UserProfile,
});

The tradeoff is a steeper setup — file-based routing convention, a build step, and a stricter way of thinking. The roadmap lists it alongside React Router [3], and the way I read it: TanStack Router is the choice when type safety around navigation and search params is worth the setup cost (large apps with complex query-param state), while React Router remains the sensible default for most projects.

aveshina.my.id/users/42 router parses URL route table match /users/:id → <UserProfile/> id = 42 <UserProfile id={42}/> mounts back/forward = undo/redo URL is state · router maps it to a component · history still works

How I use this

For most projects I default to React Router — it's battle-tested, the API is stable, and nested routes with <Outlet> cover the layout cases I hit. I treat the URL as state from day one: anything the user might want to share or bookmark (a filter, a selected tab, a detail view) goes into the URL as a path segment or search param, not just into local component state. That habit is what makes "deep links work" a side effect of how I build rather than a feature I bolt on. When a project's search-param state gets complex enough that I'm writing manual parsers and validators, that's the signal to evaluate TanStack Router — the type safety around params and search pays for itself at scale.

References

[1] LogRocket, "How to use routing in React JS: a comprehensive guide," blog.logrocket.com, 2023. [Online]. Available: https://blog.logrocket.com/react-router-v6-guide/

[2] React Router team, "React Router," 2024. [Online]. Available: https://reactrouter.com/

[3] TanStack, "TanStack Router documentation," 2024. [Online]. Available: https://tanstack.com/router/latest/docs/framework/react/overview

[4] pedrotech, "React Router v7: a crash course," dev.to, 2024. [Online]. Available: https://dev.to/pedrotech/react-router-v7-a-crash-course-2m86

Knowledge check · Question 1 of 5

In a React SPA, what is the URL?

Comments

Leave a Comment

You must be signed in to comment

0 Comments

No comments yet. Be the first to comment!