Upgrading React Router

Kent demonstrates upgrading React Router from v5 to v7. For v5 to v6, the key change is moving from flat routes with Switch to nested routing with Routes. Other updates include replacing children with Outlet, switching to function-based classNames, and using useSearchParams instead of the old URL parameter handling.
The v6 to v7 transition requires minimal changes - mainly switching to configuration objects with createBrowserRouter while keeping JSX syntax with createRoutesFromElement. Error handling needs to be hoisted up in this new structure.
Kent highlights v7's improved features: built-in error boundaries for routes, importing directly from "react-router" (as react-router-dom is now deprecated), and the component prop instead of element. He also showcases relative routes and the new NavLink render prop API.
This practical example helps developers incrementally upgrade to access React 19 features, better type safety, and the "full framework mode" that Remix brought to React Router.
Transcript
00:00 React Router is our bridge to the future of React, but some of us are on old versions of React Router, and that's why I'm so grateful that the React Router team, in addition to bridging us to the next version of React, has also given us some really great upgrade guides. So even if you're on version 5,
00:18 there's a compatibility package to help get you over to version 6, and all of this documentation to help you with whatever it is that you're using in React Router that is stopping you from upgrading to version 6. And then once you get to version 6, there is an upgrade guide to go from version 6 to version 7, and there's not a whole lot in here,
00:38 but there's a little bit, and it can be really helpful to see an example of an app that has made this transition. So I made one. This is the Grata Text app, and I'm going to be using this to teach you all about React in a whole series of React Router workshops. So we're going to learn all about React Routers here,
00:57 and this is a pretty simple app. There's not a whole lot going on in here. There are a couple of routes. We've got 404 page. We've got links to other places, and then we have this kind of list detail view here, as well as edit pages,
01:14 and including even a search here that updates the query params. So, and then, of course, we've got this add new recipient. So there's quite a few things going on in here as far as React Router is concerned, and I have taken this app,
01:31 and I have written it in version 5 of React Router, then upgraded to version 6 of React Router with as few changes as possible, then upgraded from version 6 to version 6 later with some additional features, and then from version 6 later to version 7.
01:48 And so with that, this is not actually a proper workshop. In fact, if you go to upgrade reactrouter.epicweb.dev, you'll find that I've got this notice here because it is using the Epic Workshop app, but I haven't made exercises for it, and the reason is that I don't really expect that you will need to get good
02:08 at upgrading different versions of React Router, but I do think that it's useful to use some of the tools that the workshop has, specifically the diff view, and so if you go to slash diff on any of the Epic Workshop apps, then you'll get to this page.
02:26 This is actually the page I use to develop the workshops that you watch, and it's very helpful for me as I develop the workshops, but also can be really helpful for you in seeing the difference between each of the steps as we go, and so you can take a look at the code, download it yourself, and see what does the initial state
02:46 of the v5 app look like, but here are the changes that we make to go from v5 to v6 with as few changes as possible. We actually skipped the v5, v6 compact package because there's not a whole lot going on here, but that might be another intermediary step
03:02 so that you can iterate to the full version, but here are the things that ultimately need to change to go from v5 to v6, and this is v6 like the very, very first release of v6, so not using any extra features that came in v6.30 or whatever, so we're switching from React Router DOM v5 to v6
03:22 in our package JSON, and then we go into our routes right here, and what we used to have was this flat routes with the switch component, and now we've got this routes component here with some nesting going on here,
03:39 and this was a really big improvement in v6. This is reason enough for you to go just from v5 to v6 is to get this nested routing, not just in the definition, but also in what that means with the way that we're linking to different things as well, and ultimately actually in the performance of things too,
03:57 so we typically in your applications, you're going to have kind of a layout and then like little subsections in your components are the things that are going to change, and we kind of represented this in our v5 version by having these different wrappers, so we have the marketing that has the app layout
04:15 and the marketing layout and then the component, and then we have the recipients page which has the app layout and then the recipients layout as opposed to the marketing one, and then we even have error boundaries in here. Your app may not be configured this way if you're on version 5, but it probably has that sort of look and feel,
04:35 and so you could incrementally migrate to have this sort of thing if you wanted to be more intentional about your layout routes because it will be really useful when you get into this nested routing, so switching from this flat switch format to this nested format
04:54 is probably the biggest leap that you need to make, and then the rest of the changes from going from v5 to v6 involve going from using children because layout routes weren't really a thing to embracing layout routes with the outlet component, so instead of rendering children,
05:13 we're going to render outlets. Another thing that we get from this is we ditch the active class name and we have this function to generate the class name based on the active state, and so we are doing that here, and then we also have useSearchParams is a new thing in React Router version 6,
05:32 so we can drop the useHistory and useLocation, URLSearchParams nonsense that we were doing before, and just useSearchParams and the setSearchParams that we're doing there, and that's quite nice. You'll notice also the types here got changed a little bit, and I believe that the reason for that
05:50 is because you're actually not making yourself very type-safe just by putting this here. You really have no idea whether the ID is one of the params or not. TypeScript can't know that, and so just drop those. They're not all that useful. If you want to do some runtime validation, then that's a good thing. Go ahead and do that.
06:10 And then we, on the Edit page, just some more of that. We have our layout again just switching from children to outlet because we have nested routing now, and same thing with our app layout here. So not a whole lot actually changes. The biggest thing is the route definition, and now we have nested routing.
06:27 So that is our V5 to V6 at 6.0, and then if we want to go from V6 to V6.30 or whatever, or even to React Router DOM version 7, but the first version, or the very beginning of version 7,
06:47 then we can do that without changing anything. So there's not really any difference between version 6.30 of React Router DOM and version 7 of React Router DOM as far as the way that we use it, but there are a whole lot of additional features. We're not going to be using any of those in this change.
07:05 All we're trying to do is make our thing work in version 7, and this is all that you need to do to make it work in version 7. First, actually, let's go to routes. We are now importing createBrowserRouter and createRoutesFromElement rather than importing this routes,
07:23 and that's because what used to be a component of routes is now a configuration object. And so we use createBrowserRouter and pass a configuration object to configure all of the routes that we have. But personally, I like JSX better than a configuration object,
07:40 and so we also have this createRoutesFromElement which allows us to continue with our route configuration which we had here. But one thing you'll notice here is that we don't have this unknown error boundary anymore. This is our just global error handler.
07:57 If all else fails, it's going to render this thing. Well, because we're no longer creating an app routes component, we can't actually render this anymore. These routes are not even like real React elements. They're just turned into the configuration object that createBrowserRouter wants now.
08:16 And so as a result, we can't render this, and we just simply hoist that up into our createRoute, and that works just fine. Don't worry, though. In just a second, we'll actually have an even better solution. That's pretty much it. To go from version 6 to version 7, you don't have to change anything else.
08:36 It's just createBrowserRouter, and then you can use createBrowserFromElements, and then make sure that you're handling the fact that we're no longer a component. We're a configuration object. So then we want to actually use some of the cool features that are in version 7,
08:53 or even some of these features are in later versions of version 6. And so here are the things that we need to do for that. Now React Router supports error boundaries as a built-in feature of routes, so each route can have its own error boundary. And so you may still want to keep React error boundary around for various reasons.
09:12 It's still useful for isolated areas of your components, but if you're just putting your error boundaries around routes, then you don't need to worry about React error boundary anymore. And then React Router DOM in version 7 actually just exports star from React Router, so you can actually uninstall that entirely
09:31 and use React Router itself. In fact, React Router DOM is deprecated in version 7. Everything should just be React Router. And with that, then, let's take a look at our main file here. So we're getting rid of the unknown error boundary. We're going to move that back to where it was before in the routes file.
09:50 And then instead of pulling the route provider from React Router DOM, we're just going to pull it from React Router. And then it's the same. We're giving it that configuration object of routes from our routes TSX, so let's take a look at that. Here we are bringing back in our unknown error boundary,
10:07 and we're passing it in as the root route. So this is going to be our entire application is wrapped inside of this thing, and now we have an error boundary prop on the route component. And again, this is still create routes from elements, so this turns into a configuration object that gets passed into create browser router,
10:26 which then creates this router object that we pass into the router provider. The other thing that changes in here, aside from the error boundaries, we also now can use the component prop instead of the element prop. And I personally prefer that because I think there's a possibility in the future that React Router will pass our components props
10:45 that will be useful, like params and loader data and action data and stuff like that. I'm hopeful that that's something that could happen in the future. I don't have any insider knowledge, but that seems like the sort of thing that could happen in the future. And so moving from element to component just makes a lot of sense for me there. But other than that, nothing else changed.
11:03 We are removing that error boundary wrapping and using just components and error boundary, and that's working just fine. Everything else is effectively the same. And then as far as the rest of our code goes, the biggest changes that we're going to see here are switching from React Router DOM to React Router
11:21 and updating our error boundaries to use the React Router error boundary handling. So we get our route error here. We no longer need to use the error boundary from React Error Boundary. And then we can just use this as the error boundary prop for our different components. So we're no longer accepting children
11:41 or anything like that. And then, yeah, everything else is going to be let's use relative routes. And so we've got relative links here. We also now have, with the latest versions of React Router, we have support for a render prop on our nav link component.
12:00 And so we can, instead of doing things with CSS, which arguably maybe it would be better with this group thing, I just want to show that you actually can now use the isActive. We've got this render prop API, so you can do whatever you want to with this,
12:19 not only what is displayed, but even what interactive elements can the user start working with or whatever. So that is another nice feature in the latest versions of React Router. And then, yeah, there are just a couple other things with error boundaries.
12:37 I don't think that there is really too much else. Of course, once you get to React Router version 6.4, you can start using loaders and actions, which is quite nice. We're not doing any of that in here. This is just taking what you could do in version 5 and getting you all the way up to what you can do now in version 7.
12:56 I'm not covering everything in this example. So there's usePrompt, for example, is probably one that some people are running into that you would need to use something else in React Router version 7. I think it's... Let's actually look at the API reference
13:15 and it's useBlock. useBlocker is what you would use instead of usePrompt in version 7 with all the same caveats that it doesn't handle hard reloads and stuff like that. But hopefully this gives you at least something that you can reference and have a real-ish world application
13:34 and see what is the difference, what are the things that need to happen as I'm incrementally migrating from version 5 all the way to version 7 of React Router. And that is going to pave the way for you to use the latest features of React 19, get some type safety with your routing,
13:52 go to full framework mode with React Router and all of the awesome things that Remix did for React Router. It's a really, really beautiful place. So I'm glad that you're here with me. I hope that helps you upgrade to the latest version of React Router. Now, get to it.
- Play Replace Remix's unstable_parseMultipartFormData with @mjackson/form-data-parser
Replace Remix's unstable_parseMultipartFormData with @mjackson/form-data-parser
- Play Epic Workshop Diff Tab Demo
Epic Workshop Diff Tab Demo
- Play Epic Workshop Test Tab Demo
Epic Workshop Test Tab Demo
- Play Get Started with the Epic Workshop App (for React)
Get Started with the Epic Workshop App (for React)
- Play Prisma Typed SQL Queries
Prisma Typed SQL Queries