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 know, the lead maintainers forked the React Router project to create a lightweight alternative called Reach Router in early 2018.
During this time, both libraries grew, however it seems that active development for Reach Router will stop, and will be merged into the upcoming React Router v6 🛣
With the release coming soon, here’s a sneak peek of what’s coming!
Jump to…
Big changes with
Nested Routes are simpler
useNavigate instead of useHistory
From 20kb to 8kb
is becoming
This top-level component is going to be renamed. However, its functionality is mostly remaining the same.
// v5
import {
BrowserRouter,
Switch,
Route
} from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/"><Home /></Route>
<Route path="/profile"><Profile /></Route>
</Switch>
</BrowserRouter>
);
}
Just drop
// v6
import {
BrowserRouter,
Routes,
Route
} from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="profile/*" element={<Profile />} />
</Routes>
</BrowserRouter>
);
}
Big Changes with
In v6 the
The component/render
prop will be substituted for the element
prop:
import Profile from './Profile';
// v5
<Route path=":userId" component={Profile} />
<Route
path=":userId"
render={routeProps => (
<Profile routeProps={routeProps} animate={true} />
)}
/>
// v6
<Route path=":userId" element={<Profile />} />
<Route path=":userId" element={<Profile animate={true} />} />
If you noticed, in v6 it’s much easier to pass props now. This has negated the use of the render
prop in v5.
Nested Routes are Simpler
Nested routes in v5 had to be very explicitly defined. This required including a lot of string-matching logic into these components. See
// v5
import {
BrowserRouter,
Switch,
Route,
Link,
useRouteMatch
} from 'react-router-dom';
function App() {
return (
<BrowserRouter>
<Switch>
<Route exact path="/" component={Home} />
<Route path="/profile" component={Profile} />
</Switch>
</BrowserRouter>
);
}
function Profile() {
let match = useRouteMatch();
return (
<div>
<nav>
<Link to={`${match.url}/me`}>My Profile</Link>
</nav>
<Switch>
<Route path={`${match.path}/me`}>
<MyProfile />
</Route>
<Route path={`${match.path}/:id`}>
<OthersProfile />
</Route>
</Switch>
</div>
);
}
In v6, you can remove the string-matching logic. There isn’t any need for useRouteMatch()
either! The result is pleasantly minimal:
// v6
import {
BrowserRouter,
Routes,
Route,
Link,
Outlet
} from 'react-router-dom';
// Approach #1
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="profile/*" element={<Profile/>} />
</Routes>
</BrowserRouter>
);
}
function Profile() {
return (
<div>
<nav>
<Link to="me">My Profile</Link>
</nav>
<Routes>
<Route path="me" element={<MyProfile />} />
<Route path=":id" element={<OthersProfile />} />
</Routes>
</div>
);
}
// Approach #2
// You can also define all
// <Route> in a single place
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/" element={<Home />} />
<Route path="profile" element={<Profile />}>
<Route path=":id" element={<MyProfile />} />
<Route path="me" element={<OthersProfile />} />
</Route>
</Routes>
</BrowserRouter>
);
}
function Profile() {
return (
<div>
<nav>
<Link to="me">My Profile</Link>
</nav>
<Outlet />
</div>
)
}
Note: the {this.props.children}
in React Router v6. This was a very popular feature from Reach Router!
useNavigate Instead of useHistory
Sometimes you’ll want to programmatically navigate. For example, after a user submits a form and they need to be redirected to a confirmation page. This is the useHistory
library in v5, which has been renamed to useNavigate
in v6:
// v5
import { useHistory } from 'react-router-dom';
function MyButton() {
let history = useHistory();
function handleClick() {
history.push('/home');
};
return <button onClick={handleClick}>Submit</button>;
};
Now history.push() will be replaced with navigate():
// v6
import { useNavigate } from 'react-router-dom';
function MyButton() {
let navigate = useNavigate();
function handleClick() {
navigate('/home');
};
return <button onClick={handleClick}>Submit</button>;
};
In some cases, you’ll want to replace an URL in the browser history instead of pushing a new URL. This has slightly changed with v6:
// v5
history.push('/home');
history.replace('/home');
// v6
navigate('/home');
navigate('/home', {replace: true});
From 20kb to 8kb
With all of these changes, you’d expect the bundle size to grow, but it’s actually reduced by half! The minified bundle of v5 was ~20kb, and v6 is only ~8kb.
Bundle sizes are calculated using the BundlePhobia tool.
Conclusion
I’m pretty excited about the release of React Router v6. Hopefully this article has given you an idea of what to expect when it releases (which should be soon)! You can read more about React Router v6 in the latest release notes 📝
For a complete list of new features, see the official React Router v6 migration guide 🚏