I'm thrilled to have you here today because we're about to embark on an exciting adventure into the captivating world of React Router nested routes. Trust me, I've been in your shoes, and understanding nested routes can be quite a puzzle. But fear not, because I'll be your friendly guide, leading you through this journey step by step.
By the end of this article, you'll have a solid grasp of nested routes and how they can take your React applications to the next level. We'll explore the fundamental concepts, delve into React Router v6, and even learn about the powerful useOutletContext
feature.
So, buckle up and get ready for an enlightening exploration of React Router nested routes!
useOutletContext
Are you excited? I sure am 😀 Let's dive right in and unravel the wonders of nested routes in React together!
In some websites, it's quite common to visit the about page by going to /about
. When you land there, you'll see a cool cover image and the title About. And guess what? You'll have the option to choose one of two sub-pages:
/about/us/
/about/team/
Isn't it great? You can explore more about the website and its awesome team through these sub-pages!
The About us page will display the same content as the /about
page, but it will also include an additional section called About us below. Similarly, the About the Team page will show the same content as the /about
page, but it will have an extra section called About the Team below.
This demonstrates nested routes, as the /about/us
page combines two routes into one. It includes the content from both the /about
route and the /about/us
route.
To make nested routes work in React Router, follow these three steps:
<Route />
definitions inside an existing <Route />
element.<Outlet />
component inside the route that will have nested routes.
Let's begin with an example where we have an <About />
component that renders on the /about
path.
import {BrowserRouter, Routes, Route} from "react-router-dom";
function About() {
return <>
<h1>About</h1>
</>;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/about/" element={<About />}>
</Route>
</Routes>
</BrowserRouter>
);
}
We aim to enhance the functionality of the <About />
component by enabling it to render nested routes. Essentially, whenever the path begins with /about
, we want the component to render along with its corresponding child components. This way, we can consistently display the <h1>About</h1>
heading on sub-pages like /about/us
and /about/team
.
<Route />
definitionsFirst, let's define the two nested routes under /about
. We'll create a <Route path="us" />
and a <Route path="team" />
. Remember, the paths are relative because we're nesting them inside <Route path="/about/" />
. These two <Route />
components will be children of the <Route path="/about" />.
So, the structure will look like this:
<Route path="/about/" element={<About />}>
{/* about/us */}
<Route path="us" element={<h2>About us</h2>}>
</Route>
{/* about/team */}
<Route path="team" element={<h2>About the Team</h2>}>
</Route>
</Route>
I've added some comments to help you out. So, when you have the path attribute set to us
nested inside the path attribute set to /about/
, it creates a final path for that route as /about/us
. Similarly, if you have the path attribute set to team
, it will become /about/team
.
Start the nested routes without using a /
character. Otherwise, you'll need to repeat the /about
path. So, valid paths are us
and /about/us
. However, I recommend the first approach.
<Outlet />
To make sure the nested routes are displayed, we'll need to tell the <About />
component how to render them. First, import the Outlet
component from react-router-dom
and use it within the <About />
component. This will determine the specific location in the JSX where the nested routes will be shown.
import {BrowserRouter, Routes, Route, Outlet} from "react-router-dom";
function About() {
return <>
<h1>About</h1>
{/* Render nested routes for /about/... here */}
<Outlet />
</>;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/about/" element={<About />}>
{/* about/us */}
<Route path="us" element={<h2>About us</h2>}>
</Route>
{/* about/team */}
<Route path="team" element={<h2>About the Team</h2>}>
</Route>
</Route>
</Routes>
</BrowserRouter>
);
}
If you put the <Outlet />
before the <h1>About</h1>
, then the content of the nested routes like <h2>About us</h2>
or <h2>About the Team</h2>
will be displayed before the <h1>
.
<Link />
elementsFinally, let's add the <Link />
elements so that we can easily view and test these nested routes.
import {BrowserRouter, Routes, Route, Outlet, Link} from "react-router-dom";
function About() {
return <>
{/* Content always rendered when the URL starts with /about */}
<h1>About</h1>
<Link to="us">About us</Link> |
<Link to="team">About the team</Link>
<Outlet />
</>;
}
function App() {
return (
<BrowserRouter>
<Routes>
<Route path="/about/" element={<About />}>
{/* about/us */}
<Route path="us" element={<h2>About us</h2>}>
</Route>
{/* about/team */}
<Route path="team" element={<h2>About the Team</h2>}>
</Route>
</Route>
</Route>
</Routes>
</BrowserRouter>
);
}
Sometimes, we need to pass data to the components that are rendered by the Outlet
. For now, let's consider a basic example where we have a variable called data that we want to make accessible to any component rendered under the <Outlet />
.
Instead of this basic example, If you want me to write a project based article, on how to do this let me know in the comments.
function About() {
// we want to make this available to the <Outlet /> sub-components
const data = {
someValue: 123
};
return <>
{/* Content always rendered when the URL starts with /about */}
<h1>About</h1>
<Link to="us">About us</Link> |
<Link to="team">About the team</Link>
<Outlet />
</>;
}
To achieve that, you can utilize the context attribute on the <Outlet />
component, which will automatically generate a context for you. This is a commonly used feature, and the React Router team has incorporated it directly into the library.
function About() {
// we want to make this available to the <Outlet /> sub-components
const data = {
someValue: 123
};
return <>
{/* Content always rendered when the URL starts with /about */}
<h1>About</h1>
<Link to="us">About us</Link> |
<Link to="team">About the team</Link>
// pass it to Outlet as a context
<Outlet context={data} />
</>;
}
This special context attribute is exclusive to the <Outlet />
component. It automatically creates a context for us, which can be accessed by the children of <Outlet />
. All we need to do is use it.
useOutletContext
hookThe useOutletContext
hook is a handy tool that helps us in various components that need to be rendered under the <Outlet />
. Let's take the AboutUs
component as an example.
import {useOutletContext} from "react-router-dom";
function AboutUs() {
const context = useOutletContext();
console.log(context); // {someValue: 123}
console.log(context.someValue); // 123
// ...
}
Congratulations on completing this captivating journey into the world of nested routes. I encourage you to apply what you've learned and experiment with nested routes in your own projects. Thank you for joining me on this enlightening exploration of nested routes in react. Happy coding, and may your React applications flourish with enhanced navigation and seamless user journeys!