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 it is. React Router doesn’t give you a house - it gives you some nails, plywood, and a hammer and trusts that you can do the rest. A more technical way to say that is React Router gives you the routing primitives upon which you can build your app. This concept really shines when it comes to what we’re going to do in this post – build our own custom Link component.

What we want to do is create our own “old school” navbar. Basically what that means is we’ll add a > to the front of whatever Link is active. If our two routes were / and /about, the two states of our navbar would look like this

Before we dive into our custom Link, let’s build out the skeleton of our app. We’ll have two components, Home and About, which will map to our two Routes, / and /about.

import * as React from 'react'
import {
BrowserRouter as Router,
Routes,
Route,
Link
} from 'react-router-dom'
const Home = () => <h2>Home</h2>
const About = () => <h2>About</h2>
export default function App () {
return (
<Router>
<div>
{/* Links */}
<hr/>
<Routes>
<Route path="/" element={<Home />} />
<Route path="/about" element={<About />} />
</Routes>
</div>
</Router>
)
}

Now the reason we’re here, implementing our custom Link component. We’ll call it OldSchoolMenuLink. Let’s start with the API and work backwards. Here’s what it’ll look like

export default function App() {
return (
<Router>
<div>
<OldSchoolMenuLink to="/">
Home
</OldSchoolMenuLink>
<OldSchoolMenuLink to="/about">
About
</OldSchoolMenuLink>
<hr/>
<Routes>
<Route path="/" element={<Home />}>
<Route path="/about" element={<About />}>
</Routes>
</div>
</Router>
)
}

Notice it’s the OldSchoolMenuLink that will be in charge of adding and removing the >, but its API is the same as React Router’s Link component.

Now let’s build it out. We know what props OldSchoolMenuLink is going to be taking in, so we can build out the skeleton of the component before we worry about its implementation.

function OldSchoolMenuLink ({ children, to }) {}
WTF is children?

If you’re not familiar with the “children” prop in React, it’s a placeholder for whatever is between the opening and closing element.

<Hover>Childrencanbeanything</Hover>
<Hover>{true}</Hover>
<Hover>{() => console.log('Even functions')}</Hover>

Now the main question becomes, what is OldSchoolMenuLink going to render? Remember, the whole point of this component is, based on the active route, to make this navbar UI work.

With that said, we know we’re going to render a Link and if the app’s current location matches the Links to prop, we’ll pre-pend it with a >.

In order to do that, we need to get the “app’s current location”. To do that we can use React Router’s useLocation Hook. From there, all we need to do is compare the location’s pathname with the to prop.

function OldSchoolMenuLink ({ children, to }) {
const location = useLocation()
const match = location.pathname === to
return ()
}

Now that we know if the app’s current location matches the Links path, all that’s left to do is render some UI, toggling the > based on our match variable.

function OldSchoolMenuLink ({ children, to }) {
const location = useLocation()
const match = location.pathname === to
return (
<div className={match ? 'active' : ''}>
{match ? '> ' : ''}
<Link to={to}>
{children}
</Link>
</div>
)
}

Just like that, we’ve created our own custom OldSchoolMenuLink component by composing React Router’s Link component.

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.