React Router v6 is both the next version of React Router v5 but also @reach/router… it’s a coming together of all the best things from both routers in an easy to use light-weight package. In this tutorial we’ll cover all the basics from routing, nested routes, dynamic routes, linking to catch-all routes in just over 15 minutes.
At the time of this writing, React Router v6 is still in alpha, but the time is about right to start playing with it and exploring what’s to come. This guide will give you a peek at the new features/changes!
As you may already know, there is another great routing solution for React called Reach Router. Reach Router is lightweight, easier to use, and focuses on accessibility. React Router and Reach Router will be merged together and React Router is going to be the surviving project.
We’ve seen the first efforts in v5.1 with Hooks API, but we’re starting to see the actual results with v6: nested routes, relative links, relative routes, automatic route ranking, and so on.
Note: React Router v6.0.0-alpha.2 was used at the time of writing.
Run the following command to get React Router v6:
npm install react-router@next react-router-dom@next
One of the great improvements over previous versions is the bundle size. It has been reduced by almost 70% from the previous version.
<Switch>
is replaced with <Routes>
. Thanks to this new API, we can define all our routes in one place and have automatic route ranking as well as relative routes and links.
import React from 'react';
import ReactDOM from 'react-dom';
import {
BrowserRouter as Router,
Routes,
Route
} from 'react-router-dom';
function App() {
return (
<div>
<h1>Welcome</h1>
<Routes>
<Route path="/" element={<Home />} />
<Route path="about" element={<About />} />
</Routes>
</div>
);
}
ReactDOM.render((
<Router>
<App />
</Router>
), document.getElementById('app'));
<Route>
has undergone some changes to make our lives easier.
<Route component>
was removed in favor of <Route element>
.<Route children>
was changed to accept child routes.<Route exact>
and <Route strict>
since routes are matched automatically.<Route path>
is relative to the route’s hierarchy.import { Routes, Route, Outlet } from 'react-router-dom';
function Invoices() {
return (
<div>
<h1>Invoices</h1>
{/*
This element renders the element for the child route, which in
this case will be either <SentInvoices> or <IndividualInvoice>
*/}
<Outlet />
</div>
);
}
function IndividualInvoice() {
let { invoiceId } = useParams();
return <h1>Invoice {invoiceId}</h1>;
}
function SentInvoices() {
return <h1>Sent Invoices</h1>;
}
function App() {
return (
<Routes>
<Route path="invoices" element={<Invoices />}>
<Route path=":invoiceId" element={<IndividualInvoice />} />
<Route path="sent" element={<SentInvoices />} />
</Route>
</Routes>
);
}
Just like <Route path>
, <Link to>
is also relative to the route’s hierarchy. If you omit the beginning /
, it becomes relative to the route path that it sits in.
import { Routes, Route, Link, Outlet } from 'react-router-dom';
function Home() {
return <h1>Home</h1>;
}
function Dashboard() {
return (
<div>
<h1>Dashboard</h1>
<nav>
<Link to="invoices">Invoices</Link>{" "}
<Link to="team">Team</Link>
</nav>
<hr />
<Outlet />
</div>
);
}
function Invoices() {
return <h1>Invoices</h1>;
}
function Team() {
return <h1>Team</h1>;
}
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard" element={<Dashboard />}>
<Route path="invoices" element={<Invoices />} />
<Route path="team" element={<Team />} />
</Route>
</Routes>
);
}
You have the freedom to use multiple <Routes>
in one application. It can be useful to compose small applications to work together.
import React from 'react';
import { Routes, Route } from 'react-router-dom';
function Dashboard() {
return (
<div>
<p>Look, more routes!</p>
<Routes>
<Route path="/" element={<DashboardGraphs />} />
<Route path="invoices" element={<InvoiceList />} />
</Routes>
</div>
);
}
function App() {
return (
<Routes>
<Route path="/" element={<Home />} />
<Route path="dashboard/*" element={<Dashboard />} />
</Routes>
);
}
Do you remember the react-router-config
package that enabled you to define your routes as an object? Well, that package is history. Now we havethe useRoutes
hook instead.
It’s similar to the old package with a few improvements. You define your routes as an array of objects, pass it to the custom hook, and you get a React element that you can render. That’s it.
Here is an example configuration:
function App() {
let element = useRoutes([
// These are the same as the props you provide to <Route>
{ path: '/', element: <Home /> },
{ path: 'dashboard', element: <Dashboard /> },
{ path: 'invoices',
element: <Invoices />,
// Nested routes use a children property, which is also
// the same as <Route>
children: [
{ path: ':id', element: <Invoice /> },
{ path: 'sent', element: <SentInvoices /> }
]
},
// Redirects use a redirectTo property to
{ path: 'home', redirectTo: '/' },
// Not found routes work as you'd expect
{ path: '*', element: <NotFound /> }
]);
// The returned element will render the entire element
// hierarchy with all the appropriate context it needs
return element;
}
I hope it’s not going to be a surprise for you if I say <Routes>
is just a wrapper component over useRoutes
.
useHistory
is now history. It’s been replaced with React’s suspense-ready navigate API. From now on, you can useNavigate
to navigate around. It has both imperative and declarative options.
import { Navigate, useNavigate } from 'react-router-dom';
function Declarative() {
return <Navigate to="/home" replace state={state} />;
}
function Imperative() {
let navigate = useNavigate();
function handleClick() {
navigate('/home')
}
return (
<div>
<button onClick={handleClick}>go home</button>
</div>
);
}
More and more details will surely come in the following days. No matter what, I’m already happy with what I see.
Happy coding!
Source code: https://github.com/leighhalliday/react-router-v6-demo
Getting started guide: https://github.com/ReactTraining/react-router/blob/dev/docs/installation/getting-started.md
#reactjs #webdev #javascript