Check your version
This post assumes you're using React Router v6. If not, find your version below.

One thing I love about React Router is how composable and “React like” it is. If you’re familiar with the mental model of React, then you’re probably already familiar with the mental model of React Router. This is demonstrated perfectly in the example we’re going to walk through – building a sidebar with React Router.

Specifically, here’s an example of what we’ll be building.

It’s a fairly basic app. We’ll have three components – Home, Profile, and Settings – which will map nicely to our three paths – /, /profile, and /settings.

What I want you to notice though is the text inside the sidebar. Just like the body of the app, it’s also dynamic based on the app’s current location. You’re probably used to seeing React Router dynamically swap out single components based on the app’s current location, but you usually don’t see it happen in multiple places. Let’s walk through how to do it.

First, we’ll start with the basics – our simple components and navbar.

import { Link } from 'react-router-dom'
const Home = () => <h1>Home</h1>;
const Profile = () => <h1>Profile</h1>;
const Settings = () => <h1>Settings</h1>;
export default function App() {
return (
<div className="wrapper">
<div className="sidebar">
<ul className="nav">
<li><Link to="/">Home</Link></li>
<li><Link to="/profile">Profile</Link></li>
<li><Link to="/settings">Settings</Link></li>
</ul>
</div>
</div>
);
}

Next, we need to render some Routes so React Router can know which component to render when the user visits certain a location. Before we worry about the sidebar, let’s render the main Routes for the body of the app.

import {
Link,
Routes,
Route
} from 'react-router-dom'
export default function App() {
return (
<div className="wrapper">
<div className="sidebar">
<ul className="nav">
<li><Link to="/">Home</Link></li>
<li><Link to="/profile">Profile</Link></li>
<li><Link to="/settings">Settings</Link></li>
</ul>
</div>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</div>
);
}

At this point our app is working fine, but we haven’t really done anything special. We’ve simply mapped our app’s location to a few components using React Router. Now we need to figure out the sidebar. Any ideas?

Remember, the goal here is, just like we did with the main body of our app, to make our sidebar dynamic based on the app’s location. Well, what if we just render another Routes component inside the navbar itself? Then we could create another set of Route components, mapping the app’s current location to the text we want to show in the navbar.

Can we do that…? Why not?

export default function App() {
return (
<div className="wrapper">
<div className="sidebar">
<ul className="nav">
<li><Link to="/">Home</Link></li>
<li><Link to="/profile">Profile</Link></li>
<li><Link to="/settings">Settings</Link></li>
</ul>
<Routes>
<Route
path="/"
element={
<p>
Thisisyourhomepage. You'llseeyourfeedwhichismadeup of
thepeopleyoufollow.
</p>
}
/>
<Route
path="/profile"
element={
<p>
Thisisyourprofilepage. You'llbeabletoseeallyour
profileinformation as well as thepeopleyoufollow.
</p>
}
/>
<Route
path="/settings"
element={
<p>
Thisisyoursettingspage. Youcanchangeyourname, image, and
anything else associated with youraccount.
</p>
}
/>
</Routes>
</div>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/profile" element={<Profile />} />
<Route path="/settings" element={<Settings />} />
</Routes>
</div>
);
}

Your brain might be thinking there’s something magic going on here but there’s really not. With React Router, we can render as many Routes as we’d like and the outcome is the same. Whenever the app’s location changes, any Routescomponent will look through all its children Route elements to find the best match to render. This proces is true whether we have 1 Routes or 20.


We can even clean this up a bit if you’d like. Right now we’re having to duplicate our paths in multiple places. It’s not a huge deal, but as our app grows larger, this might become an inconvenience later on.

Because React is “Just JavaScript™”, there’s nothing stopping us from encapsulating all our routing logic into its own array.

const routes = [
{
path: "/",
main: () => <Home />,
sidebar: () => (
<p>
This isyourhomepage. You'llseeyourfeedwhichismadeup of the
peopleyoufollow.
</p>
)
},
{
path: "/profile",
main: () => <Profile />,
sidebar: () => (
<p>
This isyourprofilepage. You'llbeabletoseeallyourprofile
information as well as thepeopleyoufollow.
</p>
)
},
{
path: "/settings",
main: () => <Settings />,
sidebar: () => (
<p>
This isyoursettingspage. You canchangeyourname, image, and
anything else associated with youraccount.
</p>
)
}
];

Now that we have our routes array, we can map over it whenever we want to create a new collection of Routes.

export default function App() {
return (
<div className="wrapper">
<div className="sidebar">
<ul className="nav">
<li><Link to="/">Home</Link></li>
<li><Link to="/profile">Profile</Link></li>
<li><Link to="/settings">Settings</Link></li>
</ul>
<Routes>
{routes.map(({ path, sidebar }) => (
<Route
key={path}
path={path}
element={sidebar()}
/>
))}
</Routes>
</div>
<Routes>
{routes.map(({ path, main }) => (
<Route
key={path}
path={path}
element={main()}
/>
))}
</Routes>
</div>
);
}

Because React Router allows us to render as many Routes as we’d like, and because we abstracted our routes to their own array, we can render different components at different sections of our page whenever the app’s location matches the Route’s path.

Want to learn more?
If you liked this post and want to learn more, check out our free Comprehensive Guide to React Router.

Before you leave

I know, "another newsletter pitch" - but hear me out. Most JavaScript newsletters are terrible. When's the last time you actually looked forward to getting one? Even worse, when's the last time you actually read one rather than just skim it?

We wanted to change that, which is why we created Bytes. The goal was to create a JavaScript newsletter that was both educational and entertaining. 84,338 subscribers and an almost 50% weekly open rate later, it looks like we did it.

Delivered to 84,338 developers every Monday

Avatar for Tyler McGinnis

Tyler McGinnis

CEO of ui.dev. Obsessed with teaching, writing, swimming, biking, and running.

Share this post

Want more react-router?

This is part of our React Router course. You can take the rest of the course by starting a free 3-day trial.