How to create nested dynamic routes using React Router v6

Piyush Jain
6 min readApr 20, 2022

--

In this tutorial we will learn how to create nested routed using React Router v6.

This is how our final result look like.

nested dynamic routes using React Router v6

First of all we will create 2 components one for Home and another one for User.

Now we will create 2 new router and will add navigation for them in App.Js. We will create 2 more components apart from mentioned above one for Layout and another one for NotFound.

import { Link, Routes, Route, BrowserRouter as Router } from 'react-router-dom';
import Home from './components/Home';
import NotFound from './components/NotFound';
import User from './components/User';
import Layout from './components/Layout';
import './App.css';const App = () => {return (
<Router>
<h1>React Router</h1>
<nav>
<Link to="/home">Home</Link>
<Link to="/user">User</Link>
</nav>
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path="home" element={<Home />} />
<Route path="user" element={<User />} />
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</Router>
);
}

In this functional component we have matching Link and Route component from React Router for /home and /user routes. Then we have index route loaded with Home Component and a NoFound / WildCard route loaded with NotFound component.

Both works as fallback components.

Nested Routes

In User Component we will add nested routing. So, we will add a Link components which will navigate user to their Profile and Transactions.

import React from 'react';
import { Link } from 'react-router-dom';
const User = () => {
return (
<>
<h1>User</h1>
<nav>
<Link to="profile">Profile</Link>
<Link to="transactions">Transactions</Link>
</nav>
</>
)
}
export default User;

If we click one of these link we would be redirected to NotFound route. This means we haven’t mapped these routes (/user/profile, /user/transactions) to actual route component yet. Therefore we will add these nested in routes to our /user routes.

import { Link, Routes, Route, BrowserRouter as Router } from 'react-router-dom';
import Home from './components/Home';
import NotFound from './components/NotFound';
import User from './components/User';
import Layout from './components/Layout';
import Profile from './components/Profile';
import Transactions from './components/Transactions';
const App = () => {return (
<Router>
<h1>React Router</h1>
<nav>
<Link to="/home">Home</Link>
<Link to="/user">User</Link>
</nav>
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path="home" element={<Home />} />
<Route path="user" element={<User />}>
<Route path="profile" element={<Profile />} />
<Route path="transactions" element={<Transactions />} />
</Route>
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</Router>
);
}
export default App;

The Route components map to the Link components in a one to one relationship now. However, there can be more than one Link component linking to the same route, so it’s actually a one to many relationship.

If we test this now in browser we will see that only User Component shows up in the browser not its nested component which are Profile and Transactions which clicking their respective links.

To make it work we will have to add Outlet component from React Router.

import React from 'react';
import { Link, Outlet } from 'react-router-dom';
const User = () => {
return (
<>
<h2>User</h2>
<nav>
<Link to="profile">Profile</Link>
<Link to="transactions">Transactions</Link>
</nav>
<Outlet />
</>
)
}
export default User;

The Outlet component renders the matching child route with its respective component (here either Profile or Transactions component) from the parent Routes’ component collection of Route components.

If there is not /profile or /transactions matching route (e.g. /user/profile), we will see only User component showing up. So, to avoid this we can add index and NotFound Route. Now the default route will be Profile.

const App = () => {return (
<Router>
<h1>React Router</h1>
<nav>
<Link to="/home">Home</Link>
<Link to="/user">User</Link>
</nav>
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path="home" element={<Home />} />
<Route path="user" element={<User />}>
<Route index element={<Profile />} />
<Route path="profile" element={<Profile />} />
<Route path="transactions" element={<Transactions />} />
<Route path="*" element={<NotFound />} />
</Route>
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</Router>
);
}

That’s it. While the User component always renders the navigation, its content (Outlet) gets replaced by the matching nested route (either Profile or Transactions component based on /user/profile or /user/transactions route). If none of these routes are matched when visiting the /user route, the application will show either the Profile component (if route matches exactly /user) or the NoMatch component (if route does not match, e.g. /user/setting) showing up.

Dynamic Nested Route

In this section we will render dynamic nested route based on identifiers /user/transactions/1 (Transactions details with transaction id 1).

Let’s start by initializing a list of transactions in our App Component. This is just a simple data but can be fetch from remote API. We will pass this data in Transactions Component as props.

const App = () => {
const transactions = [
{ id: '1', details: 'Transaction 1' },
{ id: '2', details: 'Transactions 2' },
];
return (
<Router>
<h1>React Router</h1>
<nav>
<Link to="/home">Home</Link>
<Link to="/user">User</Link>
</nav>
<Routes>
<Route element={<Layout />}>
<Route index element={<Home />} />
<Route path="home" element={<Home />} />
<Route path="user" element={<User />}>
<Route index element={<Profile />} />
<Route path="profile" element={<Profile />} />
<Route path="transactions" element={<Transactions transactions={transactions} />} />
<Route path="*" element={<NotFound />} />
</Route>
<Route path="*" element={<NotFound />} />
</Route>
</Routes>
</Router>
);
}

Transactions component becomes list component here, where we will be showing list of transaction for the user using Link Component. The relative path in the Link component hints to a respective nested (here: /${transaction.id} nested in /user/transactions) yet dynamic (here: /${transaction.id}) route.

const Transactions = ({ transactions }) => {
return (
<>
<h2>Transactions</h2>
<ul>
{transactions.map((transaction) => (
<li key={transaction.id}>
<Link to={transaction.id}>
{transaction.details}
</Link>
</li>
))}
</ul>

</>
)
}

Now we need to create a matching nested route in App Component. First it is Nested Route of /user/transactions, we can place it in respective parent route component. Secondly it is dynamic route, it uses dynamic route which is defined as :transactionId , where transactionId is dynamic.

Now we will add Outlet Component in Transactions Component so that it can render matched child route.

const Transactions = ({ transactions }) => {
return (
<>
<h2>Transactions</h2>
<ul>
{transactions.map((transaction) => (
<li key={transaction.id}>
<Link to={transaction.id}>
{transaction.details}
</Link>
</li>
))}
</ul>
<Outlet />
</>
)
}

Now we will create Transaction Component, which will be render in Transaction Component via Outlet whenever there is a matched route.

import React from 'react';
import { Link, useParams } from 'react-router-dom';
const Transaction = () => {
const { transactionId } = useParams();
return (
<>
<h2>Transaction Id: { transactionId }</h2>
<Link to={-1}>Back to Transactions</Link>
</>
)
}
export default Transaction;

Thats it. In Transaction Component we are getting transaction id via useParam Hook which we defined as Dynamic in App Component.

According to React Router doc

The useParams hook returns an object of key/value pairs of the dynamic params from the current URL that were matched by the <Route path>. Child routes inherit all params from their parent routes.

Also we have added a Back to Transactions link in Transaction Component which will redirect back to /user/transactions route.

That it. In this article we have seen how to create nested routes and dynamic route using react router.

Nested Routes and Dynamic Routes help you to enhance your experience and maintain structured routes.

If you liked this please give a clap.

Source Code: https://github.com/piyush303/react-router-v6-nested-routes-demo

Demo: https://codesandbox.io/s/nested-dynamic-routes-using-react-router-v6-9gs2uq

Let us know which topic you want us to cover in next articles.

Read Next

--

--

Piyush Jain

An avid front-end technologist with 9+ years of web industry experience. Self-motivated professional with broad technical skill-set and very strong attention.