This article is the translation, the original address at the end of the article, if there are translation problems welcome to correct and exchange!
Today we are pleased to announce the stable version of React Router V6.
This version has been around for a long time. The last time we released a major API change was four years ago, in March 2017, when we released version 4. Some of you may not have been born yet. Needless to say, a lot has happened since then:
- React Router downloads grew more than 60 times (6000%) from 340,000 / month in March 2017 to 21 million/month in October 2021.
- We released version 5 with no major changes (I’ve written elsewhere about why major changes hit).
- We launched the Reach Router and currently average about 13 million downloads per month
- React Hooks introduced
- COVID-19
I could easily write at least a few pages about each of these points and its importance to our business and the open source projects we’ve been managing since 2014. But I don’t want you to get tired of it. We’ve all been through a lot in the past few years. Some of it was tough, but hopefully you experienced some new growth, as we did. In fact, we completely changed our business model!
Today, I want to focus on the future and how we can leverage past experiences to build the strongest future possible for the React Router project and the incredible React community. There will be code. But we’ll also talk about the business and what you expect from us (hint: it’s very colorful).
Why another major release?
The biggest reason for releasing new routers is easily the emergence of React hooks. You may remember Ryan’s talk at React Conf 2018 introducing the world to hooks, and how much of the code we used to write using React’s “lifecycle methods” disappeared when you refactored your Class-based React code into hooks. If you don’t remember that speech, you should probably stop here and take a look. I’ll wait.
React Router V6 is truly re-implemented using React Hook, although we added some hooks in V5 (V5.1). They are such efficient low-level primitives that we were able to eliminate a lot of boilerplate code by using hooks. This means that your V6 code will be more compact and elegant than your V5 code.
Also, it’s not just that your code becomes smaller and more efficient… The same goes for our implementation! Our compressed gzip package size is down by more than 50% in V6! The React Router now adds less than 4KB to your total application package, and the actual result is even smaller once you run it through your packer tool and turn on tree-shaking.
Combinable router
To demonstrate how to improve your code using hooks in V6, let’s start with something very simple, such as accessing parameters from the current URL pathname. React Router v6 provides a useParams()hooks (also in 5.1) that allow you to access the current URL parameters wherever you need them.
import { Routes, Route, useParams } from "react-router-dom";
function App() {
return (
<Routes>
<Route path="blog/:id" element={<BlogPost />} / ></Routes>
);
}
function BlogPost() {
// You can access the params here...
const { id } = useParams();
return (
<>
<PostHeader />{/ *... * /}</>
);
}
function PostHeader() {
// or here. Just call the hook wherever you need it.
let { id } = useParams();
}
Copy the code
How to implement the same functionality with Render Prop or higher-order components in V5 or previous versions.
// React Router v5 code
import * as React from "react";
import { Switch, Route } from "react-router-dom";
class App extends React.Component {
render() {
return (
<Switch>
<Route
path="blog/:id"
render={({ match}) = > (
// Manually forward the prop to the <BlogPost>.<BlogPost id={match.params.id} />)} / ></Switch>
);
}
}
class BlogPost extends React.Component {
render() {
return (
<>{/ *... and manually pass props down to children... booo */}<PostHeader id={this.props.id} />
</>); }}Copy the code
Hooks eliminate the need for
to access router internal state (match related logic) and to manually pass props to pass that state to child components.
Another way to say this is to consider useParams() something like useState() in the Router context. The Router knows some state (the current URL parameter) and allows you to access it at any time using hooks. Without hooks, we need to manually pass state to descendant components.
Let’s look at another simple example of how React Router V6 using hooks is much more powerful than V5. Suppose you want to send a “PageView” event to your analytics service (similar to Google Analysis) when the current location changes. In V6(V5.1 +), useLocation()hooks can help you quickly implement this function:
import { useEffect } from "react";
import { useLocation } from "react-router-dom";
function App() {
const location = useLocation();
useEffect(() = > {
window.ga("set"."page", location.pathname + location.search);
window.ga("send"."pageview");
}, [location]);
}
Copy the code
Of course, thanks to the combination of capabilities provided by hooks, you can wrap all of this in one hooks, for example:
import { useAnalyticsTracking } from "./analytics";
function App() {
useAnalyticsTracking();
// ...
}
Copy the code
Also, without hooks, you would have to do some hacks like render a separate
render null so you can access location when it changes. Also, without useEffect(), you must implement componentDidMount+componentDidUpdate to ensure that page browsing events are sent only when location changes.
// React Router v5 code
import * as React from "react";
import { Switch, Route } from "react-router-dom";
class PageviewTracker extends React.Component {
trackPageview() {
let { location } = this.props;
window.ga("set"."page", location.pathname + location.search);
window.ga("send"."pageview");
}
componentDidMount() {
this.trackPageview();
}
componentDidUpdate(prevProps) {
if(prevProps.location ! = =this.props.location) {
this.trackPageview(); }}render() {
return null; // lol}}class App extends React.Component {
return (
<>{/* This route isn't really a piece of the UI, it's just here so we can access the current location... * /}<Route path="/" component={PageviewTracker} />
<Switch>{/* your actual routes... * /}</Switch>
</>
);
}
Copy the code
This code is crazy, right? Well, this is processing logic that can’t be avoided without hooks.
To recap: We’re releasing a major new release of the React Router so you can publish smaller, more efficient applications for a better user experience. It really is that simple.
You can see a full list of hooks available in V6 in our API documentation.
Still using React.Component? Don’t worry, we still support class components! See this GitHub thread for more information.
Improved routing
Remember the react-nested-router? Probably not. But that’s what the React router was called before the React-router officially got the package name on NPM. . The React Router has always declared nested routines, although the way we express them has changed slightly over time. I’ll show you what we propose for V6, but first let me give you some back story about V3, V4/5, and the Reach Router.
In V3, we nested the
element directly into a huge routing configuration, as shown in this example. Nesting
elements is a good way to visualize the entire Route hierarchy. However, our implementation in V3 made it difficult to split the code because all the routing components ended up in the same package (this was the previous react.lazy ()). So as you add more routes, your bundle grows. In addition,
prop makes it difficult to pass custom props to your components.
In V4, we optimized for large applications. Code splitting! Instead of
nesting elements in V4, you simply nest your own component and place another component in a
child. You can see how it works in this example. This makes it easy to build large applications, because the code for splitting the React Router application is the same as splitting any other React application, and you can do this in React using one of the different tools available at the time, which are not routers related to React. However, an unexpected disadvantage of this approach is that it
will only match the beginning of the URL pathname, because each routing component may have more child routes somewhere deep. So React Router V5 applications must use
every time they have no child routes (per leaf Route).
In our experimental Reach Router project, we borrowed the Preact Router idea and ran automatic route rankings to try to find the route that best matches the URL, regardless of the order in which it was defined. This is a significant improvement over the V5
element and helps developers avoid the pitfalls of defining routes in the wrong order and creating routes that are not accessible. However, the Reach Router’s lack of a
component causes some pain when working with TypeScript, because each of your routing components must also accept routing-specific props, such as PATH (see more on that here).
So, after all the changes, what does React Router V6 really need? Well, ideally, we’d have the best features of each of the apis we’ve explored so far, while avoiding the problems they’ve encountered. Specifically, we want to:
<Route>
We have the readability of juxtaposed, nested S in V3, but also support for code splitting and passing custom props to your routing components- The flexibility we had in V4/5 to split routes across multiple components without the need
exact
Pass props around - We have the ability to rank routes in the Reach Router without messing with prop types of routing components
Oh, we would also like the object-based routing API, in V3 we allow routing to be used as normal JavaScript objects, not as in V4/5. You can only use
elements, static match, and render functions. If you want to use them like objects, you also need the react-router-config package.
Well, needless to say, we’re happy to roll out a routing API that meets all of these requirements. Check out the V6 API documentation on our website. It actually looks a lot like V3:
import { render } from "react-dom";
import { BrowserRouter } from "react-router-dom";
// import your route components too
render(
<BrowserRouter>
<Routes>
<Route path="/" element={<App />} ><Route index element={<Home />} / ><Route path="teams" element={<Teams />} ><Route path=":teamId" element={<Team />} / ><Route path="new" element={<NewTeamForm />} / ><Route index element={<LeagueStandings />} / ></Route>
</Route>
</Routes>
</BrowserRouter>.document.getElementById("root"));Copy the code
But if you look closely, you’ll see that our work over the years has led to some subtle improvements:
- We are using
<Routes>
Rather than<Switch>
. Instead of scanning the routes sequentially, instead<Routes>
Automatically selects the best route for the current URL. It also allows you to pass routes throughout the application instead of<Router>
They are all predefined as prop, as we did in V3. - the
<Route element>
Prop allows you to customize custom props (evenchildren
) to your route Elements. If it’s aReact.lazy()
Component, which can also be easily passed<React.Suspense>
Lazy loading of your routing elements. We are inFrom the INSTRUCTIONS for the V5 upgradeIntroduced in detail<Route element>
Advantages of APIS. - It does not need to be declared on the leaf node of the route
<Route exact>
To ensure that depth matching is possible, you can define route path as*
To select deep matching, so you can split your route config file as follows
import { Routes, Route } from "react-router-dom";
function App() {
return (
<Routes>
<Route path="/" element={<Layout />} ><Route path="users" element={<Users />} ><Route index element={<UsersIndex />} />
{/* This route will match /users/*, allowing more routing
to happen in the <UsersSplat> component */}
<Route path="*" element={<UsersSplat />} / ></Route>
</Route>
</Routes>
);
}
function UsersSplat() {
// More routes here! These won't be defined until this component
// mounts, preserving the dynamic routing semantics we had in v5.
// All paths defined here are relative to /users since this element
// renders inside /users/*
return (
<Routes>
<Route path=":id" element={<UserProfile />} ><Route path="friends" element={<UserFriends />} / ><Route path="messages" element={<UserMessages />} / ></Route>
</Routes>
);
}
Copy the code
I’d love to show you more routing apis here, but it’s hard to cover them all in a blog post. But fortunately, you can read the code.
So, I’ll link to some examples that I hope will speak louder than what I’ve written here. Each example has a button that allows you to launch it in the online editor so that you can use it.
- Our “basic” examples make you familiar
<Routes>
.<Route>
.<Outlet>
, and<Link>
The API - Use JavaScript objects to define routes instead of
<Route>
The element - Use lazy loading (code splitting) for individual routing elements and nested routines
React.lazy()
- use
basename
Define portable React Router applications in the same code base, with each application “installed” on a different URL pathname prefix
Check out the rest of the V6 examples here, and be sure to send us PR if you don’t have one you want to see!
Another feature we borrowed from V3 is support for layout routing in the form of a new
element. You can read more about the layout in the V6 Overview.
This is really the most flexible and powerful routing API we’ve ever designed, and we’re very excited about the applications we’re building using it.
Relative routing and linking
Another major improvement in React Router V6 is relative to
and
. We’ve devoted a long section of the React Router V5 Upgrade Guide to guide you through it. For relative
and
, it can basically be summarized as follows:
- The relative
<Route path>
The value of (path
Always relative toThe parent route. You no longer have to build them from /. - The relative
<Link to>
The value of (to
) is always relative to route path. If it contains only one search string (i.e<Link to="? framework=react">
), which is the pathname relative to the current location. - The relative
<Link to>
The value of (to
) than<a href>
Is clearer and always points to the same path, whether the current URL has a slash or not
See also the description of values in the V5 Upgrade guide for more information on why relative values (to) point to more clearly than values and how to use the front.. Segment returns the parent route.
Relative routing and linking make the ease of use of the Router a big step forward because you don’t need to build absolute
and
paths in nested routines. Really, this is the way it should always work, and we think you’ll really enjoy the simplicity and intuition of building applications this way.
Note: Absolute paths still work in V6 to help simplify upgrades. You can even ignore relative paths entirely and use absolute paths forever if you wish. We won’t mind.
Upgrade to React Router v6
We want to be very clear about this: React Router V6 is the successor to all previous React Router versions, including V3 and V4/5. It is also the successor to the Reach Router. We encourage all users of the React Router and Reach Router to upgrade to V6 whenever possible. We have some big plans for V6, and we don’t want you to be left out when we introduce some really cool stuff in 6.x! Yes, even if you v3 users insist on your onEnterhooks you won’t want to miss this.
However, we realized that getting everyone to upgrade was a very ambitious goal for a library that was being downloaded 34 million times a month. We are already developing a backward compatibility layer for React Router V5 users and will soon be testing it with multiple customers. Our plan is to develop a similar layer for Reach Router users as well. If you have a large application and are worried about upgrading to V6, don’t worry. Our backward compatibility layer is in development. Also, V5 will continue to receive updates for the foreseeable future, so take your time.
If you can’t wait and want to upgrade yourself, here are some links to help:
- Upgrade from V5 to V6
- Migrate from Reach Router to V6
In addition to the official upgrade guide, I’ve published some comments to help you start the migration slowly. Keep in mind that the goal of any migration is to be able to get some work done and then release it. No one likes a long-running upgrade branch!
Here are some instructions on deprecated patterns and fixes you can implement in V5 applications today before attempting to upgrade to V6:
- Redirects are handled in V6
- Combined in V6
<Route>
Also, don’t feel pressured to perform the migration process. We think React Router V6 is the best Router we’ve ever built, but there may be some issues you need to deal with as you work. We are here to help you when you are ready to upgrade.
If you are a Reach Router user, you are worried about losing the accessibility features it provides, and we are working on that as well. It turns out that the Reach Router’s automatic focus management is actually worse than doing nothing in some cases. We realized that in order to manage focus properly, we needed more information than just location changes. However, it was a worthwhile experiment and we learned a lot. Our next project will help you build applications that are more accessible than ever before…
Future: want to
The React Router provides the foundation for many of today’s great and impressive Web applications. It was an amazing feeling for me to open up the developer console for web applications like Netflix, Twitter, Linear, or Coinbase and see the React Router being used for their flagship applications. Each of these companies has a great talent and resource pool, and they and many others have chosen to build their businesses on React and React Router.
One thing people really like about the React Router is that it does the job of routing perfectly without causing problems. It was never really intended to be a closed framework, so it could accommodate any technology stack. Maybe you’re a server renderer, maybe you’re not. Maybe you’re breaking code, maybe you’re not. Maybe you’re rendering a dynamic site with client routing and data, or maybe you’re just rendering a bunch of static pages. React Router is happy to do whatever you want.
But how do you build the rest of the application? Routing is only part of the story. What about data loading and mutations? What about caching and performance? Should I do server rendering? What’s the best way to split code? How should you deploy and host your application?
As it happens, we have some very strong opinions about all of this. That’s why we’re building Remix, a new web framework that will help you build better websites.
In recent years, as Web applications have become more complex, front-end Web development teams have assumed far more responsibility than ever before. They don’t just need to know how to write HTML, CSS, and JavaScript. They also need to understand TypeScript, compilers, and build pipelines. In addition, they need to know about Bundlers and Code Splitting, and how the application loads when the customer visits the site. There’s a lot to think about! Remix and the amazing Remix community will give your team extra help in managing and making informed choices about how to do all this and more.
We’ve been working on Remix for over a year and recently secured some funding and hired a team to help us build it. We will release the code under the open source license before the end of the year. React Router V6 is the heart of Remix. As Remix moves forward and gets better, so do routers. You’ll continue to see a steady stream of releases and improvements from us on React Router and Remix.
We are very grateful for all the support we have received to date, as well as the many friends and customers who have trusted us over the years. We sincerely hope you enjoy using React Router V6 and Remix!