React Router v6 - NavLink v5 typed wrapper

Viewed 114

I couldn't find the answer I was looking for so I thought I would make my own post so people could find it.

For those who couldn't work out the type for the NavLink v5 wrapper for react-router v6 but needed to use it so styled-components could read the activeClassName.

As far as I could tell you cannot use NavLink (v6) without this wrapper when styling it through styled-components

import React, { CSSProperties } from 'react'
import { NavLink as BaseNavLink, NavLinkProps } from 'react-router-dom'

/**
 * This is a wrapper for the v6 version of NavLink to keep the v5 functionality
 * See here for base - https://reactrouter.com/docs/en/v6/components/nav-link#navlink
 *
 * This is needed so styled components can use active className as it doesn't port the ({ isActive })
 */

type NavLinkWrapperProps = NavLinkProps & {
  activeClassName?: string
  activeStyle?: CSSProperties
}

export const NavLink = ({ activeClassName, activeStyle, ...props }: NavLinkWrapperProps) => {
  return (
    <BaseNavLink
      {...props}
      className={({ isActive }) => [props.className, isActive ? activeClassName : null].filter(Boolean).join(' ')}
      style={({ isActive }) => ({
        ...props.style,
        ...(isActive ? activeStyle : null),
      })}
    />
  )
}
1 Answers

You absolutely don't need to do anything special to make the react-router-dom@6 NavLink component work with the styled-components library.

Example:

import { NavLink } from "react-router-dom";
import styled from "styled-components";

const StyledNavLink = styled(NavLink)`
  text-decoration: none;
  padding: 1rem;

  &.active {
    color: red;
    font-size: 32px;
  }
`;

export default function App() {
  return (
    <div className="App">
      <StyledNavLink to="/">Home</StyledNavLink>
      <StyledNavLink to="/test">Test</StyledNavLink>
    </div>
  );
}

enter image description here

Edit react-router-v6-navlink-v5-typed-wrapper

If you however are trying to maintain the RRDv5 API syntax you must create a custom NavLink component with the previous version's activeClassName and activeStyle props.

Example:

import { CSSProperties, forwardRef } from "react";
import {
  NavLink as BaseNavLink,
  NavLinkProps as BaseNavLinkProps
} from "react-router-dom";
import styled from "styled-components";

interface NavLinkProps extends BaseNavLinkProps {
  activeClassName?: string;
  activeStyle?: CSSProperties;
}

const NavLink = forwardRef<HTMLAnchorElement, NavLinkProps>(
  (
    { activeClassName = "active", activeStyle, ...props }: NavLinkProps,
    ref
  ) => (
    <BaseNavLink
      ref={ref}
      {...props}
      className={({ isActive }) =>
        [props.className, isActive ? activeClassName : null]
          .filter(Boolean)
          .join(" ")
      }
      style={({ isActive }) => ({
        ...props.style,
        ...(isActive ? activeStyle : null)
      })}
    />
  )
);

const StyledNavLink = styled(NavLink)`
  text-decoration: none;
  padding: 1rem;

  &.foo {
    color: red;
    font-size: 32px;
  }
`;

export default function App() {
  return (
    <div className="App">
      <StyledNavLink activeClassName="foo" to="/">
        Home
      </StyledNavLink>
      <StyledNavLink
        activeClassName="foo"
        activeStyle={{
          color: "green"
        }}
        to="/test"
      >
        Test
      </StyledNavLink>
    </div>
  );
}

enter image description here

Edit react-router-v6-navlink-v5-typed-wrapper (forked)

Related