Building a Navbar with React-Router, React-Bootstrap, and TypeScript

I’m building a React SPA and using react-router-dom for routing, and react-bootstrap as a component library. For a simple header, I had several goals:

  1. Active nav links are styled (you can tell which one you’re on, and hovering the link to the page you’re on does not show a pointer or hover style; but links to other pages do)
  2. Clicking the active link (or the Navbar.Brand component, when on the home page) should not reload the page you’re already on
  3. On mobile, clicking a nav menu item should collapse on click
  4. Use TypeScript

These three goals seemed very straightforward and not contradictory; but I had a hard time making them play nicely together.

The Problem

To detect the active links, the NavLink component of react-router-dom has an isActive property you can reference in an inline style, for example:

style={({ isActive }) => ({ cursor: isActive ? 'default' : 'pointer' })}

However, to get the react-bootstrap Navbar menu to collapseOnSelect, you have to use the react-bootstrap Nav.Link component, not NavLink.

You also can’t nest a Nav.Link within a NavLink; nesting <a> tags breaks the HTML standards.

I tried many iterations; getting one to work using “as” to render a different component than I specified:

<Nav.Link as={NavLink} eventKey="1" style={({ isActive }) => ({ cursor: isActive ? 'default' : 'pointer' })} to="/">

…until I tried to add TypeScript. Then it was just barking about the conflicting properties between those two definitions.

The Solution

Enter another library:

import { LinkContainer } from 'react-router-bootstrap';

Then the code was blissfully simple, working properly, and conforming to TypeScript! I had to add in the forced active={false} since both libraries apply an “active” property, which leaves multiple links appearing “active” after navigating. Best to disable the Nav.Link ones and just let LinkContainer do the work.

<Nav>
    <LinkContainer to="/">
        <Nav.Link active={false}>Home</Nav.Link>
    </LinkContainer>
    <LinkContainer to="/about">
        <Nav.Link active={false}>About</Nav.Link>
    </LinkContainer>
</Nav>

Leave a Reply

Your email address will not be published.