What is react-router?
The react-router npm package is a declarative routing library for React, allowing you to add navigation functionality to your React applications. It enables you to handle URL routing, match routes to your React components, and manage navigation state in a single-page application (SPA) environment.
What are react-router's main functionalities?
Basic Routing
This code demonstrates how to set up basic routing in a React application using react-router. It includes navigation links and route components that render different components based on the URL path.
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
function App() {
return (
<Router>
<div>
<nav>
<ul>
<li>
<Link to='/'>Home</Link>
</li>
<li>
<Link to='/about'>About</Link>
</li>
</ul>
</nav>
<Route exact path='/' component={Home} />
<Route path='/about' component={About} />
</div>
</Router>
);
}
function Home() {
return <h2>Home</h2>;
}
function About() {
return <h2>About</h2>;
}
Dynamic Routing
This code snippet shows how to implement dynamic routing with path parameters. The User component will render with the appropriate user ID based on the URL.
import { BrowserRouter as Router, Route, Link } from 'react-router-dom';
function App() {
return (
<Router>
<div>
<ul>
<li>
<Link to='/users/1'>User 1</Link>
</li>
<li>
<Link to='/users/2'>User 2</Link>
</li>
</ul>
<Route path='/users/:id' component={User} />
</div>
</Router>
);
}
function User({ match }) {
return <h2>User ID: {match.params.id}</h2>;
}
Nested Routing
Nested routing allows you to create routes within routes. This example shows a Layout component with a nested Dashboard route.
import { BrowserRouter as Router, Route, Link, Switch } from 'react-router-dom';
function App() {
return (
<Router>
<Route path='/' component={Layout} />
</Router>
);
}
function Layout({ match }) {
return (
<div>
<nav>
<Link to={`${match.url}dashboard`}>Dashboard</Link>
</nav>
<Switch>
<Route path={`${match.path}dashboard`} component={Dashboard} />
</Switch>
</div>
);
}
function Dashboard() {
return <h2>Dashboard</h2>;
}
Protected Routes
Protected routes are used to restrict access to certain parts of your application. This example shows a route that renders a component only if the user is authenticated, otherwise it redirects to a login page.
import { BrowserRouter as Router, Route, Redirect } from 'react-router-dom';
function App() {
return (
<Router>
<Route path='/protected' render={() => (
isAuthenticated() ? (
<ProtectedComponent />
) : (
<Redirect to='/login' />
)
)} />
</Router>
);
}
function isAuthenticated() {
// Authentication logic here
return true;
}
function ProtectedComponent() {
return <h2>Protected</h2>;
}
Other packages similar to react-router
vue-router
Vue-router is the official router for Vue.js. It provides similar functionalities for Vue applications as react-router does for React applications, including nested routes, dynamic segments, and navigation guards. However, it is designed to work seamlessly with Vue's reactivity system.
reach-router
Reach Router is another routing library for React, which aims to be more accessible and simpler to use than react-router. It has a smaller API surface area and focuses on accessibility by managing focus after route transitions. However, as of my knowledge cutoff in 2023, Reach Router has been officially merged with React Router, and its features have been integrated into React Router v6.
react-router
is the primary package in the React Router project.
Installation
npm i react-router
v7.6.1
Date: 2025-05-25
Patch Changes
-
react-router
- Partially revert optimization added in 7.1.4
to reduce calls to matchRoutes
because it surfaced other issues (#13562)
-
react-router
- Update Route.MetaArgs
to reflect that data
can be potentially undefined
(#13563)
- This is primarily for cases where a route
loader
threw an error to it's own ErrorBoundary
, but it also arises in the case of a 404 which renders the root ErrorBoundary
/meta
but the root loader
did not run because not routes matched
-
react-router
- Avoid initial fetcher execution 404 error when Lazy Route Discovery is interrupted by a navigation (#13564)
-
react-router
- Properly href
replaces splats *
(#13593)
href("/products/*", { "*": "/1/edit" }); // -> /products/1/edit
-
@react-router/architect
- Update @architect/functions
from ^5.2.0
to ^7.0.0
(#13556)
-
@react-router/dev
- Prevent typegen with route files that are outside the app/
directory (#12996)
-
@react-router/dev
- Add additional logging to build
command output when cleaning assets from server build (#13547)
-
@react-router/dev
- Don't clean assets from server build when build.ssrEmitAssets
has been enabled in Vite config (#13547)
-
@react-router/dev
- Fix typegen when same route is used at multiple paths (#13574)
-
For example, routes/route.tsx
is used at 4 different paths here:
import { type RouteConfig, route } from "@react-router/dev/routes";
export default [
route("base/:base", "routes/base.tsx", [
route("home/:home", "routes/route.tsx", { id: "home" }),
route("changelog/:changelog", "routes/route.tsx", { id: "changelog" }),
route("splat/*", "routes/route.tsx", { id: "splat" }),
]),
route("other/:other", "routes/route.tsx", { id: "other" }),
] satisfies RouteConfig;
-
Previously, typegen would arbitrarily pick one of these paths to be the "winner" and generate types for the route module based on that path
-
Now, typegen creates unions as necessary for alternate paths for the same route file
-
@react-router/dev
- Better types for params
(#13543)
-
For example:
// routes.ts
import { type RouteConfig, route } from "@react-router/dev/routes";
export default [
route("parent/:p", "routes/parent.tsx", [
route("route/:r", "routes/route.tsx", [
route("child1/:c1a/:c1b", "routes/child1.tsx"),
route("child2/:c2a/:c2b", "routes/child2.tsx"),
]),
]),
] satisfies RouteConfig;
-
Previously, params
for routes/route
were calculated as { p: string, r: string }
.
-
This incorrectly ignores params that could come from child routes
-
If visiting /parent/1/route/2/child1/3/4
, the actual params passed to routes/route
will have a type of { p: string, r: string, c1a: string, c1b: string }
-
Now, params
are aware of child routes and autocompletion will include child params as optionals:
params.|
// ^ cursor is here and you ask for autocompletion
// p: string
// r: string
// c1a?: string
// c1b?: string
// c2a?: string
// c2b?: string
-
You can also narrow the types for params
as it is implemented as a normalized union of params for each page that includes routes/route
:
if (typeof params.c1a === 'string') {
params.|
// ^ cursor is here and you ask for autocompletion
// p: string
// r: string
// c1a: string
// c1b: string
}
-
@react-router/dev
- Fix href
for optional segments (#13595)
-
Type generation now expands paths with optionals into their corresponding non-optional paths
-
For example, the path /user/:id?
gets expanded into /user
and /user/:id
to more closely model visitable URLs
-
href
then uses these expanded (non-optional) paths to construct type-safe paths for your app:
// original: /user/:id?
// expanded: /user & /user/:id
href("/user"); // ā
href("/user/:id", { id: 1 }); // ā
-
This becomes even more important for static optional paths where there wasn't a good way to indicate whether the optional should be included in the resulting path:
// original: /products/:id/detail?
// before
href("/products/:id/detail?"); // ā How can we tell `href` to include or omit `detail?` segment with a complex API?
// now
// expanded: /products/:id & /products/:id/detail
href("/product/:id"); // ā
href("/product/:id/detail"); // ā
Unstable Changes
ā ļø Unstable features are not recommended for production use
@react-router/dev
- Renamed internal react-router/route-module
export to react-router/internal
(#13543)
@react-router/dev
- Removed Info
export from generated +types/*
files (#13543)
@react-router/dev
- Normalize dirent entry path across node versions when generating SRI manifest (#13591)
Full Changelog: v7.6.0...v7.6.1