DEV Community

Cover image for React Router v6 – The Complete Guide to What’s New (With v5 vs v6 Code Examples)
Vishwark
Vishwark

Posted on

React Router v6 – The Complete Guide to What’s New (With v5 vs v6 Code Examples)

React Router v6 is one of the biggest rewrites in the router’s history. It’s smaller, faster, more powerful, and built for the modern React world. If you're upgrading from v5 or starting a new project, this guide gives you a complete, practical overview of what’s changed — with side-by-side v5 vs v6 code examples for every major concept.

At the end, we’ll also preview the new Data APIs introduced in React Router 6.4+, which fully change how routing + data loading works. We’ll explore those in the next blog.


1. Smarter Route Matching (No More exact or Ordering)

React Router v6 introduces a ranking-based matching algorithm, eliminating the need for exact or manually ordering routes.

✅ Improvements

  • No more exact
  • No strict dependency on order
  • Most specific route always wins
  • Static paths outrank dynamic paths

v5 Example (ordering + exact required)

<Switch>
  <Route exact path="/" component={Home} />
  <Route exact path="/users" component={Users} />
  <Route path="/users/:id" component={UserDetails} />
</Switch>
Enter fullscreen mode Exit fullscreen mode

v6 Example (ranking-based matching)

<Routes>
  <Route path="/" element={<Home />} />
  <Route path="/users" element={<Users />} />
  <Route path="/users/:id" element={<UserDetails />} />
</Routes>
Enter fullscreen mode Exit fullscreen mode

2. <Switch><Routes>

<Routes> replaces <Switch> and uses the new ranking algorithm to pick the best match.


v5

<Switch>
  <Route path="/about" component={About} />
  <Route path="/" component={Home} />
</Switch>
Enter fullscreen mode Exit fullscreen mode

v6

<Routes>
  <Route path="/about" element={<About />} />
  <Route path="/" element={<Home />} />
</Routes>
Enter fullscreen mode Exit fullscreen mode

3. Route API Changes (component, render, children → element)

This is one of the biggest differences between v5 and v6.

React Router v5 offered three different ways to render components:
component, render, and function-as-children.

React Router v6 simplifies all of this to just one:
element={<Component />}.


v5: Three Rendering Options

1) component prop

<Route path="/about" component={About} />
Enter fullscreen mode Exit fullscreen mode
  • Must be a component reference
  • Remounts on route change

2) render prop

<Route
  path="/profile"
  render={(props) => <Profile {...props} mode="dark" />}
/>
Enter fullscreen mode Exit fullscreen mode
  • Lets you pass custom props
  • Creates a new element on every render

3) Function-as-children

<Route path="/settings">
  {(props) => <Settings {...props} />}
</Route>
Enter fullscreen mode Exit fullscreen mode
  • Runs even when the route doesn't match
  • Common source of bugs

v6: One Clean Way → element

<Route path="/about" element={<About />} />
<Route path="/profile" element={<Profile mode="dark" />} />
Enter fullscreen mode Exit fullscreen mode

Why this is better?

  • Predictable lifecycle
  • Easy to pass props
  • Cleaner API
  • Component trees behave naturally

4. Smaller Bundle Size (Up to ~60% smaller)

React Router v6 is a complete rewrite with better tree-shaking and modern architecture.
Many apps see bundle size reductions of 50–60%.

Nothing to change — just enjoy the faster loads 🚀


5. Config-Based Routing With useRoutes() (No More react-router-config)

v6 has built-in support for object-based route definitions.


v5

import { renderRoutes } from "react-router-config";

const routes = [
  { path: "/", component: Home },
  { path: "/users/:id", component: UserDetails }
];

renderRoutes(routes);
Enter fullscreen mode Exit fullscreen mode

v6

const routes = [
  { path: "/", element: <Home /> },
  { path: "users/:id", element: <UserDetails /> }
];

function App() {
  return useRoutes(routes);
}
Enter fullscreen mode Exit fullscreen mode

Cleaner + native + perfect for dynamic routing.


6. Navigation: useHistoryuseNavigate

useNavigate() replaces useHistory() and handles all navigation cases.


v5 Navigation

const history = useHistory();
history.push("/home");
history.goBack();
history.goForward();
history.go(2);
Enter fullscreen mode Exit fullscreen mode

v6 Navigation

const navigate = useNavigate();
navigate("/home");
navigate(-1); // goBack
navigate(1);  // goForward
navigate(2);  // go(2)
Enter fullscreen mode Exit fullscreen mode

One function. All navigation.


7. Active Nav Styling Updated

activeClassName and activeStyle are removed in v6.


v5

<NavLink
  to="/"
  activeClassName="active"
  activeStyle={{ color: "red" }}
>
  Home
</NavLink>
Enter fullscreen mode Exit fullscreen mode

v6

<NavLink
  to="/"
  className={({ isActive }) => isActive ? "active" : ""}
  style={({ isActive }) => ({
    color: isActive ? "red" : "black",
  })}
>
  Home
</NavLink>
Enter fullscreen mode Exit fullscreen mode

More control + more flexibility.


8. Redirect → Navigate


v5

<Redirect to="/login" />
Enter fullscreen mode Exit fullscreen mode

v6

<Navigate to="/login" replace />
Enter fullscreen mode Exit fullscreen mode

9. Nested Routing + <Outlet>

Nested routing is dramatically improved in v6.


v5 (manual, repetitive paths)

<Switch>
  <Route path="/dashboard" component={Dashboard} />
</Switch>

// Dashboard.js
<Switch>
  <Route path="/dashboard/settings" component={Settings} />
</Switch>
Enter fullscreen mode Exit fullscreen mode

You had to repeat /dashboard for every nested route.


v6 (clean, declarative)

<Routes>
  <Route path="dashboard" element={<Dashboard />}>
    <Route index element={<Overview />} />
    <Route path="settings" element={<Settings />} />
  </Route>
</Routes>
Enter fullscreen mode Exit fullscreen mode

Inside Dashboard:

<Outlet />
Enter fullscreen mode Exit fullscreen mode

Beautiful routing ✨


10. Relative Routing by Default


v5

<Link to="/dashboard/settings">Settings</Link>
Enter fullscreen mode Exit fullscreen mode

v6

<Link to="settings">Settings</Link>
Enter fullscreen mode Exit fullscreen mode

Automatically resolves relative to the parent route.


11. Removed APIs (Hook-Based Modern Router)

React Router v6 removes:

  • withRouter()
  • Prompt
  • activeClassName / activeStyle
  • component, render, and function-children in <Route>
  • Implicit nesting behavior

The API is smaller, simpler, and easier to understand.


12. Index Routes

Default child routes are now a first-class concept.


v5 Equivalent

<Route exact path="/dashboard" component={Overview} />
Enter fullscreen mode Exit fullscreen mode

v6

<Route path="dashboard" element={<Dashboard />}>
  <Route index element={<Overview />} />
</Route>
Enter fullscreen mode Exit fullscreen mode

13. Final Section: React Router’s New Data APIs (Preview Only)

React Router 6.4+ introduced Data APIs, which completely redefine how React apps handle:

  • Data loading
  • Data mutations
  • Forms
  • Navigation side effects
  • Error boundaries
  • Background fetches
  • Deferred loading

Here’s a small teaser:

const router = createBrowserRouter([
  {
    path: "/users",
    loader: async () => {
      return fetch("/api/users");
    },
    element: <Users />
  }
]);
Enter fullscreen mode Exit fullscreen mode

These APIs are powerful enough to replace a lot of manual fetching and state management.


Next Blog: Deep Dive into React Router’s Data APIs

In the next article, we’ll explore:

  • loader
  • action
  • useLoaderData
  • useActionData
  • useFetcher
  • <Form />
  • Error boundary structure
  • Deferred data
  • Real-world patterns

If you want a complete mental model of modern routing + data fetching in React, don’t miss the next post.


Top comments (0)