- React Router version 6 was released and this is quite important because React Router
is one of the most used and most important React packages that you find out there. A lot of React projects need routing and therefore, a lot of React
projects do use React Router. In this video, I will walk you through what's new with React Router version 6 and of course, I will also show you how you could update an existing React app that's using React Router version 5 to React Router version 6. Now, to learn about
React Router version 6, you can, of course, check
out the official website, ReactRouter.com, and the
documentation you find there. And specifically, there
also is a upgrading guide, which I will attach to this video where you will find
detailed upgrading steps and where you also learn
what's new and what changed. And this looks like a super long document, and if you wanna have all the details, you should definitely also dive into it. But in the end, it's
really simple to upgrade, and not a lot changed when it comes to the code that we write. Under the hood, version 6 is a lot better than version 5, and therefore, if you can upgrade, you should, of course,
strongly consider doing that. Now, to see what changed
and write some code, I created a little snapshot, a little project snapshot, which you find attached, which does use React Router version 5. So which does not use version 6. So once you downloaded and
extracted this snapshot, you should run npm install to install all the core dependencies that come with that project. And once you did that, you should install React Router version 6. And you do this by running
npm install react-router-dom, and that's important. You want react-router-dom, which is the browser
version of React Router. And then add @6, which ensures that you
install the latest version 6. Or you add @latest, which will always give you
the very latest version, and that's what we can do here. So with that installed, we
got React Router version 6. And if I now run npm start, you will see that this project won't work. If I try to visit this page, I now get an error that Switch is not exported
from react-router-dom. And that's one of the first changes. When using React Router version 5, we used this Switch component, which is provided by
the react-router package to wrap our routes and make sure that only one
of these routes is loaded at the same time, instead of all matching routes. And typically, that is what you want. You want to define multiple routes but only one route should be active at a given point of time. Now, with React Router version 6, Switch doesn't exist anymore. Instead, this now becomes Routes. So you replace Switch with Routes. And you therefore import Routes from react-router-dom instead of Switch. By the way, what hasn't changed is that you still import BrowserRouter from react-router-dom, and you wrap that around your app where you then plan to use routing. That's exactly the same
syntax as you know it. With that, the error goes away. But if I click Welcome or Products, you see that the URL changes but I still don't see
anything on the screen. The reason for this is that the way how we define our routes also changed. We still have the route component and it still takes a path prop, but the component that should be loaded when a given path becomes active, so in this case, the welcome
component, for example, is not longer a child of your route but instead, on Route, you add a new element prop, and then you pass a
dynamic value to element and that dynamic value is that to be rendered component as JSX. That's important. So you don't pass Welcome like this, you don't pass a pointer
to that component function. Instead, you pass the JSX element. Hence the name element. It wants a JSX element. And then this can, of course, become a self-closing component, this route component. And we do the same here for products. Here we also add the element prop, and we then move Products into this prop or as a value, we set it
as a value for this prop. And we turn this into a
self-closing component. And last but not least, we also do this for this last route. And please note that
this is a dynamic route and that still works as you learned it. So you can still have
dynamic path parameters, like productId as you
could with version five. That did not change. With that, once the routes
were restructured like this, now you see I see this
welcome page content here, and I can switch to
products back and forth. If I select a product, I also do see those product details. So those detail routes also works and therefore, a simple
app using routes like this can easily be updated to version 6 by replacing Switch with Routes, and moving the two below that component into this new element prop. But that's not all you
should know about version 6. In addition to these changes, it's also worth noting that there were some internal changes with React Router version 6. Specifically, the internal
logic for evaluating these paths and then picking a route to load changed. With React Router version 5, we needed to add exact here because without exact, this would match if a path
started with /products. With version 6, this behavior is gone and hence, the exact prop is gone. It now always looks for exact matches if you define your paths like this. So we can remove exact with version 6, and we still get the
same behavior as before. If you compare that to
the version 5 behavior where without exact you would
load the products component and the product detail
component if you pick a detail. That, of course, is an improvement because you don't need
that extra prop anymore. If you want that old behavior
of matching the start of a path only, you can still get that by
adding /* after your path. Once you add that, this route will become active if a URL
path starts with /products instead of being only /products. However, what you will note is that even if you do that, and you go back to your site here, if you pick a product detail, you still get the product detail instead of the products overview page, even though I'm now looking
for the start of the path. The reason for that is that internally, React Router version 6 now
also has a better algorithm for picking the best route to be loaded for a given path. And what it basically does here is it sees that if your path is
/products/ some other value, that the best fit for
this kind of path probably is this route because you explicitly
declared this route here where you say that for
/products/ something, you want ProductDetail. So unlike with version 5, the order of routes also
doesn't matter anymore. With version 5, you had to be careful. For example, if you also had another route that should be /products/edit. Then with version 5,
this kind of route here would have to be defined
before you have the route with the dynamic segment because if you had it thereafter, this route would always win. With version 6, this changed and it's smarter internally. You could define these two routes, and if you then visit /edit here, so products/edit, React Router would find out that this last route
here is the best match for this specific path. And if you had /products/p1,
on the other hand, it would determine that you have no better
match than this route, and hence, this route would become active. So the order of routes
doesn't matter anymore when working with React Router version 6. And that's another
important thing to note. I'll also revert this here because it didn't have
any effect here anyways. So these are a couple of
super important changes but as you see, code wise, there wasn't a lot to change. Now, let's move from
route definition to links. The first important thing to note is that you still have the Link component, which generally works as you learned it and you also still have
the NavLink component. That did not change. However, what did change is
this activeClassName prop. With React Router version 5, you could use that prop to apply some CSS class
automatically to the link once it became active. With React Router version 5, this prop was removed. Instead, if you want to
apply a specific class once this is active, you have to manually find out whether this link is active or not. Doing that is very simple though because you can use the className prop or the style prop to
apply dynamic styling. And both props work in a special way when applied to NavLink. className does not just take a class name, instead, it takes a function,
which you can pass here, and this function will actually
give you some information about this link and the
current state of navigation. So get some NavData here you could say. This argument is provided by React Router to this function when
this function is executed. And this function will be
executed by React Router when it evaluates NavLink and whenever your navigation changes. Inside this navData object, which you get by React Router, you then find a isActive property. And that will be true if this link is active for the
currently chosen path or not. So you can then use this to
return a className dynamically that should be applied to NavLink. So here, I can get rid
of those curly braces to use this short form of
this arrow function syntax. I could then check if
navData.isActive is truthy. In which case, I can
apply my active class here and otherwise, I apply no class. That's what I could do here. And I can do the same here for the other NavLink. It's a bit more verbose but it also gives you more
customization possibilities and more control and therefore, it is a change
that's not too difficult to incorporate, and you then
still get the old behavior as you can now see in the navigation here. So that's an important thing
you should know about links. I also wanna have a brief look at the ProductDetail component here. The ProductDetail page to be precise. There I'm getting this dynamic parameter, which is part of the URL. And with React Router version 5, we extracted that with the useParams hook that was provided by the
react-router-dom package. As you can tell, this
code still seems to work because we had no problems
extracting that dynamic parameter with React Router version 6 as well. So the code here stays the same. Now, let's move on to a
slightly more advanced app here, In this snapshot, we also have a redirect, and we have a nested route
in the Welcome.js file. Now, here we also got
some noteworthy changes that come with React Router version 6. And let's start with redirect because that's already
causing an error here because the redirect component
doesn't exist anymore. Now, you can still redirect, of course. The only thing that changed is that now instead of
redirect, it's Navigate. That's a new component
provided by React Router 6, and we can use this
here to well, navigate. And, of course, we then also
should use this element prop instead of passing it as a child between the opening
and closing route tags. Though to be precise, if
we just add it like this, what we will actually do is we will push a navigation to this page onto the navigation stack. If we truly want to redirect, so replace the current
page with the new page, then we would have to add the replace prop to Navigate as well. And this would then be
the full replacement for the old redirect component. But you could also just
configure it like this to just push the new page, instead of replacing the current page. With that, this error goes away. And as you see, if I now enter just slash, I am redirected to welcome. Now I get another error though. And that other error is
not related to redirecting because that works with
Navigate as you saw it here. Instead this error is now related to this nested route, which we have here inside another component. Nested routes can be useful because they allow you to build deeply nested page structures but the syntax you know from version 5 from React Router version 5
doesn't work like this anymore. However, updating it is also simple. The key thing you have to do here, which you also learn in this error message is that you need to wrap
your Route with Routes. In version 5, you didn't need to do that. You didn't have to wrap your
Route definition components with the Routes or Switch component. With version 6, this is mandatory though. Hence, we should import
Routes here as well. And wrap this single Route with Routes. So even if it's just one route, you have to wrap it. That's just something you have to do. Now then, we, of course, also
have to update this syntax to use the element prop, and pass our to be displayed JSX code to element instead of
having it as a child. And with this update done, if we now save this, this works, we don't
get the error anymore. But it also doesn't really work if you take a closer look. We see the welcome page, which is this title but we don't see the content here, Welcome, new user! Well, the reason for this is that I haven't loaded new-user yet. If I do change my path
to /welcome/new-user, then it also doesn't work. Now, what's wrong here? Well, what's wrong is that the logic and the syntax you need for
nested routes also changed, and it actually changed to the better. If you plan on using nested
routes, you, first of all, have to start at the parent route that will eventually
hold more nested routes. In this case, the Welcome route here. I mentioned before that
you can add this /* thing if React Router should match this route for the start of the path
instead of the full path. At the moment, I don't have this, I just have /welcome. And therefore, because of how
React Router version 6 works, this will always match and
load this welcome component if the entire path is exactly /welcome. Now, here it's /welcome/new-user though, and therefore, this route isn't loaded. So to load it, we could
change it to /new-user but that would not be the main idea here. We want to keep /welcome and only load this title
if we visit just /welcome but then we want to be
able to, in addition, load this content here, this paragraph if we have the /new-user
part after welcome. To make this possible, we have to embrace this new syntax of adding /* here. Now the welcome component will be loaded whenever our path starts with /welcome instead of having an exact full match. So therefore with this, you now see if I visit /welcome/new-user, we have our title back. But we still don't see
that paragraph in here. That's happening because of
another change introduced by React Router version 6. When you're working with nested routes, so with this kind of matching and then routes defined inside of the loaded component, as we have it here, then the paths of these nested routes actually are relative to the path of the parent route. So that means that this route
here only becomes active if our path starts with /welcome. Therefore, the path here already assumes that we have /welcome at the beginning. Hence here, we only have
to add the extra part, the extra elements in the path that are relevant for this route. We don't need to repeat /welcome/ anymore, we can just have new-user here. And that then tells React Router that this should become active if this component is active, which will be the case
for /welcome/ something. And then this paragraph should be shown if we have /welcome/new-user
because this is relative. Hence now if I save this, now we have our paragraph here for /welcome/new-user. If I just had /welcome, we would only have the title because of this relative matching. And therefore, that's
also not too difficult to update and it's actually a good change because we can keep our
nested route paths shorter, and we don't need to
repeat the entire full path all the time. Instead, we can focus on the path elements that do differ and that are relevant for identifying this route. As a side note, if you had a link in here, then the to prop here
would also be relative. So in the past with
React Router version 5, you would have to write
welcome/new-user here if you wanted to load this route and then here you would
write your link like this with version 5. With version 6, since this
is a link inside a component that already is loaded through a route, through this route here, this changes and that's relative as well. So links and routes are relative if they are loaded inside of a component that is already loaded
because of another route. So if you're in a nested route, so to say. So therefore, that's how
you could create such a link and hence, new-user, this link works as you would expect it to work. That's also something worth noting. Now, with all the changes
I covered up to this point, I already did cover pretty
much all important changes you should be aware of. And you hopefully see that upgrading really isn't too difficult. There is another important
change and pattern that I wanna explore
together with you though. We can define nested
routes as we do it here, and there's nothing wrong with it but you could also write
this here differently. Instead of defining your
routes in the component where you want to load
that nested route content, you could also go for
a different approach. You could cut this route
here from Welcome.js, and remove this Routes wrapper and remove these imports therefore, and go to your main route
definition file, the App.js file with the App component in this case. And then you could also define
nested routes right here in one and the same place. So here for the Welcome component where we wanna have a nested route, we can now actually use opening and closing route component tags, and put our nested routes between those opening and
closing tags as children. And that's also the reason why
we have this new element prop where we point at the
to below that component because the children
of the route component, if we have any, are no longer
the to below that components but instead, possible nested routes. And, of course, not every
route will have a nested route. Here in this example, most routes don't have a nested route but if you do have a nested route, as we have it here, then the advantage of
defining it like this is that you have all your
route definitions in one place. And it can simply be easier
to see all supported routes and to work on this application if you have all your route
definitions in one place instead of spreading them
across multiple components as you did have to do it
with React Router version 5. That's where you could also define your nested route like this here. And what I explained about the
path and so on still applies. We still have path=new-user here because it's nested inside
of this welcome route and hence, this will actually
be /welcome/new-user. Now, if you use this pattern though, you have to tell React Router where this nested content
should be inserted into the DOM because it was clear when
we had the route definition in the nested component, now that we have the route
definition in App.js, it's not clear where exactly in Welcome.js this
paragraph should be added. Should it be added here? Should it be added here after the link? How would React Router know? To let React Router know where that nested content
should be inserted, there is another new component, which you can import
from react-router-dom. And that's the Outlet component. You can add it like this, and this simply is a
placeholder telling React Router where nested content, nested route content should be inserted. So we can add this here in Welcome. And with our nested route definition here, this, therefore, will still
work as it did before. You see I can visit the welcome page with just /welcome, and I don't see the nested paragraph. But if I click this link, I do see it. And this is an alternative
to defining nested routes inside of other components. You could still do that, as you saw it a couple of minutes ago but you can also go for
this Outlet approach with all your routes defined in one central place, which might make your
life a little bit easier. It's up to you which approach you prefer. Now, to conclude these
updating instructions, there are two last notes I wanna make. The first note is about
imperative navigation, instead of having links
with which you navigate. Sometimes you want to navigate when a certain action finishes, when a button was clicked, when an HTTP request was sent. And with React Router version 5, you could navigate by
using the useHistory hook, and you could then execute
this hook in your component, and that would give you a history object. And then on this history object, you could push a new route or replace a route to redirect. That's the version 5 syntax. With version 6, useHistory
doesn't exist anymore. Instead now, we have a useNavigate hook. This gives us a navigate
object and function if we execute useNavigate, and this navigate function can be executed to navigate somewhere else. For example, if we would want
to programmatically navigate to /welcome, this is how we could do it. And, of course, we would not do that right in a component like this typically. Instead, we would do
that in a useEffect hook or when an HTTP request finished or anywhere else. But that's how you could
navigate programmatically. If you want to redirect, you can pass a second argument, which is an object with some options and there you could set replace to true, which will replace the current
route with the new one, which will redirect instead
of pushing the new route onto the navigation stack. Now, besides passing a
path here to navigate, you can also pass in numbers like -1 to, for example, go back
to the previous page or -2 to go to the page
before the previous page. Or one to go forward again. So forward and backward
navigation is also possible with this navigate function. You simply pass numbers to it that tell React Router by how many pages you wanna
go forward or backward. So that's programmatic
or imperative navigation with React Router version 6. Now, as a last note, I wanna talk about the prompt component, which is provided by
React Router version 5. You could use this component to prevent accidentally leaving a page if you have unsaved changes, for example. In React Router version 6, at least at this point of time when React Router version 6 was released, this component does not exist. So if you wanna prevent navigation in case of unsaved changes, you have to implement your
own workaround for the moment. So for this reason, you might wanna stick to version 5 if this is
a super important feature of your application,
and you're not willing to come up with your own logic on how you could prevent navigation. This component will probably
come back in the future as it sounds but at the moment, it's not part of React Router. And that's just something to note. But with that, that's it
for this upgrade video or these upgrade instructions and I hope this was helpful for understanding what changed, and what this means for you, and how you can update your project. Attached, you also find a
more complex project updated to React Router version six, And that should then also help you explore and practice how updating works and how React Router version 6 works.