React router DOM v6

Photo by Jean-Frederic Fortier

Routing

Routing plays a vital role in Single Page Applications (SPA) which have multiple views and also this navigation should happen without refreshing the browser to give a seamless experience to the users. In this post we are going to discuss about react-router-dom, a routing library for react.

React Router is a collection of React components, hooks and utilities that make it easy to build multi-page applications with React. It runs everywhere where React runs.
  1. react-router-dom - for web (React)
  2. react-router-native - for mobile (React Native)
  3. In server with nodejs using <StaticRouter>

Setup

In react application use npm install react-router-dom history or yarn add react-router-dom history command to install the library.

Both react-router-dom and react-router-native automatically include react-router as a dependency when you install them, and both packages re-export everything from react-router.

history is an open source library developed by react-router team. It is the main package which react-router depends on. Many of the core functionalities like Location, To, Path, State comes directly from history.

Usage

BrowserRouter

To get the react router working, we need to provide a router context provider near the root of the document. This context provider comes in many ways

  1. <BrowserRouter> - Recommended interface for browser applications.
  2. <StaticRouter> - For server-rendering a website
  3. <NativeRouter> - For react native applications
  4. <MemoryRouter> - Mostly useful for testing purposes, where it is not connected to history stack in the browser.

We are going to use BrowserRouter to create router and it is imported as:

1import { BrowserRouter as Router } from 'react-router-dom'
2...
3
4const App = () => {
5...
6return (
7 <Router>
8 // routing configurations and other components
9 </Router>
10)
11}

Router is the community standard way of naming BrowserRouter or other router context providers. Now all the components at any level( thanks to React Context API ) within Router will have access to all react-router-dom features.

Routes and Route

In version 5 of react-router-dom it was Switch and now in version 6 it is renamed to Routes.

<Routes> and <Route> are the primary ways to render something in React Router.

Route primary takes two props, path and component. path takes a string and component takes the react component to render when the browser URL hits the path specified in the Route. Route acts like an if statement for routes.

Internally, the routes specified inside Routes are passed into useRoutes (will be discussed later in this post) that in turn renders the routes. source
1<Routes>
2 <Route path="/" element={<h1>Hello</h1>} />
3 <Route path="about" element={<AboutPage />} />
4 <Route path="*" element={<NotFound />} />
5</Routes>

path

path prop takes a string which tells react-router-dom the browser path to show a component. It is relative to the root.

If none of the specified path matches the browser path, it renders the element specified in the wildcard route (path="*"). It must be placed as the last Route option.

From the above example:

1<Routes>
2 <Route path="/" element={<h1>Hello</h1>} />
3 // renders the element when https://www.example.com/
4 <Route path="about" element={<AboutPage />} />
5 // renders the element when https://www.example.com/about
6 <Route path="/contact" element={<AboutPage />} />
7 // renders the element when https://www.example.com/contact
8 <Route path="*" element={<NotFound />} />
9 // renders the element when https://www.example.com/mismatched-route
10</Routes>

useParams

Params can be passed to a route by specifying : in the path string. This way we can achieve dynamic routing.

1<Route path=":id" element={<UserDetail />} />
2// htts://example.com/1534

This can be read in the component with the help of a hook called useParams, which returns the object with the property name as the name given next to : in path.

1import { useParams } from 'react-router-dom'
2
3const UserDetails = () => {
4 const params = useParams()
5 // {id: 1534}
6 ...
7}

Nested Routes

The nested routes are specified as children to parent Route.

1<Routes>
2 <Route path="/user" element={<User />}>
3 <Route path="/" element={<UserDetails />} />
4 // renders the element when https://www.example.com/user
5 <Route path="/all" element={<UserList />} />
6 // renders the element when https://www.example.com/user/all
7 </Route>
8</Routes>

Outlet

Outlet is a placeholder in the parent of nested routes, which says where child routes have to render. For the above example, User component will look something like:

1const User = () => {
2 ...
3
4 return (
5 ...
6 <Outlet />
7 ...
8 );
9}

Line 6 is the place where the nested components will render, if the path matches the browser path.

Domain driven routing

Domain Driven Design (DDD) says, our design is how the software works.

Instead of using <Outlet /> component as placeholder for children routes, we can specify the children routes in the place where we need to render them.

Now the above example can be modified as:

1const User = () => {
2 ...
3 return (
4 ...
5 <Routes>
6 <Route path="/" element={<UserDetails />} />
7 // renders the element when https://www.example.com/user
8 <Route path="/all" element={<UserList />} />
9 // renders the element when https://www.example.com/user/all
10 </Routes>
11 )
12}

While using domain driven route, its parent route path must be suffixed with *.

1<Routes>
2 <Route path="/" element={<Home />} />
3 <Route path="/user*" element={<User />} />
4 <Route path="/contact" element={<Contact />} />

NavLink and Link

NavLink and Link are the components which help us navigate between views. The difference between NavLink and Link is, in NavLink a classname called active will be added to <a> tag in the browser. NavLink is specifically used for navigation menus such as breadcrumbs or a set of tabs where you'd like to show which of them is currently selected.

to is the prop which takes a string, it navigates to the specified path on click.

To change the active nav class to a different class in NavLink, we can use activeClassName prop.

1import React from 'react'
2import { NavLink } from 'react-router-dom'
3
4function NavList() {
5 let activeStyle = {
6 textDecoration: 'underline',
7 }
8
9 return (
10 <div>
11 <Link to="/home">Home</Link>
12 <nav>
13 <ul>
14 <li>
15 <NavLink
16 to="/messages"
17 activeClassName="current"
18 activeStyle={activeStyle}
19 >
20 Messages
21 </NavLink>
22 </li>
23 <li>
24 <NavLink
25 to="/tasks"
26 activeClassName="current"
27 activeStyle={activeStyle}
28 >
29 Tasks
30 </NavLink>
31 </li>
32 </ul>
33 </nav>
34 </div>
35 )
36}

While using <NavLink>use end prop to configure for exact match. Else all partially matching routes will have active class (if location is /home then / and /home will be treated as same, because / partially matches /home).

1<li>
2 <NavLink to="/" end>
3 Tasks
4 </NavLink>
5</li>

And while adding path, if to prop is not prefixed with / then its path will be relative to its parent. If / is prefixed, then it is an absolute path.

1// User.jsx, which is the view for "/user"
2<li>
3 // it will navigate to https://example.com/user/details
4 <Link to="details" end>
5 Details
6 </Link>
7 // it will navigate to https://example.com/home
8 <Link to="/home" end>
9 🏡 Home
10 </Link>
11</li>

useNavigate

useNavigate is the hook provided by react-router-dom to programmatically navigate between views.

It takes path to route as first parameter. Second parameter takes state and replace. state is used to pass some additional metadata to the route and repalce is to take the current route from the history stack.

1import {useNavigation} from `react-router-dom`;
2
3
4const User = () => {
5 const navigate= useNavigate();
6 ...
7 const handleSave = () => {
8 navigate("/success-page", {
9 state: {
10 saved: true
11 }
12 });
13 }
14
15 const handleLogout = () => {
16 navigate("/", {
17 state: {
18 logout: true
19 },
20 replace: true,
21 });
22 }
23 ...
24}

useLocation

useLocation hook gives details about the current location and its metadata like pathname, search, hash and importantly state. The states passed from one route to another router are read via useLocation hook.

Back and Forward navigation

Navigating back and forward is made easy with the help of useNavigate hook. As internally it uses browser History API, it uses a similar approach.

Passing delta as -1 and 1 to useNavigate function will perform back and forward operation respectively.

1import {useNavigation} from `react-router-dom`;
2
3
4const User = () => {
5 const navigate= useNavigate();
6 ...
7 const handleBack = () => {
8 navigate(-1);
9 }
10
11 const handleForward = () => {
12 navigate(1);
13 }
14 ...
15}

useRoutes

React router DOM v6 allows us to define the router configuration via Javascript objects. It is achievable through useRoutes hook. The return value of useRouter is either a component or null if the route did not match any config.

1const element = useRoutes([
2 {
3 path: '/user',
4 element: <User />,
5 children: [
6 {
7 path: '/',
8 element: <UserDetails />,
9 },
10 {
11 path: '/all',
12 element: <UserList />,
13 },
14 ],
15 },
16 {
17 path: '*',
18 element: <NotFound />,
19 },
20])
21
22return element

useSearchParams

useSearchParams is a hook used to get and set search params in the URL. Like useState, it returns an array of two items. First item gives the search params and the second item is a function which sets the search params.

1// www.example.com/user?name=pranesh&fav=pikachu
2
3import { useSearchParams } from 'react-router-dom'
4
5const User = () => {
6 const [searchParams, setSearchParams] = useSearchParams()
7
8 console.log(searchParams.get('name')) // pranesh
9}

setSearchParams re-renders the component with updated query params. It works like navigate function from useNavigate and also takes a second argument as navigate.

1// www.example.com/user?name=pranesh&fav=pikachu
2
3import { useSearchParams } from 'react-router-dom'
4
5const User = () => {
6 const [searchParams, setSearchParams] = useSearchParams()
7
8 const handleSubmit = () => {
9 event.preventDefault()
10 const searchParamsFromForm = getSearchParams(event.target)
11 // serializes the form data to object
12
13 setSearchParams(searchParamsFromForm)
14 }
15}

Outro

React router DOM v6 was concentrated much on reducing the bundle size and they achieved it. Compared to its previous versions, version 6 has much easier declarative APIs that helps navigation easier.

In another blog post we will discuss about the auth and suspense of react router DOM.

07/10/2021
All posts
Built with ❤️ and  Gatsby