Back to React
React
easy
junior

How do you implement routing in React with react router?

Wrap the app in a router, declare routes (path → element), navigate with `<Link>` / `useNavigate`, read params via `useParams`/`useSearchParams`. v6+ supports nested routes, loaders, and data routers.

6 min read·~12 min to think through

react-router-dom v6+ has two ways to declare routes:

  1. Element tree (<Routes>/<Route>) — JSX-driven, simple.
  2. Data router (createBrowserRouter) — config-driven, supports loaders/actions, best for new apps.

The hooks you'll touch:

  • useNavigate() — programmatic navigation, replaces v5's history.push.
  • useParams() — read URL segments (/users/:id{ id }).
  • useSearchParams() — read/write the query string.
  • useLocation() — current URL incl. state.

Patterns to know:

  • Nested routes + <Outlet/> for shared layouts.
  • Protected routes — a wrapper that checks auth and redirects with <Navigate to="/login" replace />.
  • 404 — a catch-all path="*" route.
  • Code splitting — lazy + Suspense per route to keep the main bundle small.

In Next.js / Remix, you don't use react-router — file-system routing and the framework router replace it. Mention both worlds; senior interviewers often ask "would you use react-router or a framework router?" and want the tradeoff.

Code

tsx
<BrowserRouter>
  <Routes>
    <Route path="/" element={<Layout />}>
      <Route index element={<Home />} />
      <Route path="dashboard" element={<RequireAuth><Dashboard/></RequireAuth>} />
      <Route path="users/:id" element={<UserPage />} />
      <Route path="*" element={<NotFound />} />
    </Route>
  </Routes>
</BrowserRouter>

function RequireAuth({ children }: { children: ReactNode }) {
  const user = useAuth();
  return user ? children : <Navigate to="/login" replace />;
}
v6 element tree with nested layout and protected route
tsx
function User() {
  const { id } = useParams();
  const [params, setParams] = useSearchParams();
  const tab = params.get("tab") ?? "overview";
  const nav = useNavigate();
  return (
    <>
      <h1>User {id} — {tab}</h1>
      <button onClick={() => setParams({ tab: "settings" })}>Settings</button>
      <button onClick={() => nav("/", { replace: true })}>Home</button>
    </>
  );
}
Reading params + programmatic nav

Follow-up questions

  • What does the data router add over <Routes>?
  • How does react-router differ from Next.js App Router?
  • How do you preserve scroll position across navigation?

Common mistakes

  • Using `<a href>` for in-app navigation — full page reload, loses state.
  • Forgetting `replace` on auth redirects — back button bounces the user back into the protected page.
  • Putting a router inside a router (e.g. by accident) — leads to weird match behavior.

Performance considerations

  • Lazy-load route elements; the entire app shouldn't ship to render `/`.
  • Use `<Link prefetch>`-style hover prefetch in your own router or framework where supported.

Edge cases

  • Trailing slashes — react-router doesn't normalize by default; either pick canonical URLs and redirect or accept both.
  • Hash routing (`HashRouter`) bypasses server config but is bad for SEO; use only when you can't control the server.

Real-world examples

  • Most CRA-based dashboards still use react-router. Greenfield SSR apps usually pick Next or Remix instead.

Senior engineer discussion

Senior signal: explain when to graduate from react-router to a framework, the loader/action data-flow model, and how SPA routing impacts SEO and analytics.

Related questions