skip to Main Content

I am trying to use styled-components with NavLink from react-router-dom@6.

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

export const StyledNavLink = styled(BaseNavLink)``;

and then use above StyledNavLink like below

<Styled.StyledNavLink
  className={({ isActive }:any) => (isActive ? "active" : "")}
  to="/contact"
>
  Contact
</Styled.StyledNavLink>

But in the UI when the link is generated, its class looks like below,

<a aria-current="page" class="sc-hLseeU befAUD ({
 isActive
}) => isActive ? &quot;active&quot; : &quot;&quot; active" href="#/contact">
  Contact
</a>

Please check the class attribute of the <a> tag. It has all the function definition in addition to the active class.

This never used to happen with react-router-dom@5, but it’s happening after migrating to react-router-dom@6.

Demo link: https://codesandbox.io/s/confident-cori-fmy9zk

2

Answers


  1. The main reason why the styled NavLink is adding the function definition to the class attribute is because of the way that styled-components works. styled-components uses a technique called tagged template literals to create the CSS styles for a component which means that the CSS styles are actually JavaScript code, and the function definition is part of that code.

    In React Router Dom v6, the NavLink component now uses a prop called activeClassName to specify the class that is applied to the NavLink when it is active. This means that the function definition is no longer needed in the class attribute, because the activeClassName prop will take care of it.

    Here’s the code which I have changed

    import styled from "styled-components";
    import { NavLink as BaseNavLink } from "react-router-dom";
    
    export const StyledNavLink = styled(BaseNavLink)`
      
    `;
    
    const NavLink = ({ to, activeClassName }) => (
      <Styled.StyledNavLink
        className={activeClassName}
        to={to}
      >
        ...
      </Styled.StyledNavLink>
    );
    
    export default NavLink;
    

    I think this will remove the function definition from the class attribute, and the NavLink will be styled correctly.

    Login or Signup to reply.
  2. The styled-component styled utility overrides the className prop. It’s stringifying any passed className function and injecting it into the DOM. To get around this you’ll need to implement some custom logic to apply the NavLink className logic on another prop that won’t be overwritten and then merge it along with the regular string className prop that styled injects.

    NavLink.styled.ts is renamed to NavLink.styled.tsx so TSX is validly used in the file.

    import styled from "styled-components";
    import { NavLink as BaseNavLink } from "react-router-dom";
    import type { NavLinkProps as BaseNavLinkProps } from "react-router-dom";
    
    interface NavLinkProps extends BaseNavLinkProps {
      customClassName?:
        | string
        | ((props: {
            isActive: boolean;
            isPending?: boolean;
          }) => string | undefined);
    }
    
    const NavLink = ({ customClassName, className, ...props }: NavLinkProps) => {
      const getCustomClassName = (props) => {
        switch (typeof customClassName) {
          case "function":
            return customClassName(props);
    
          case "string":
            return customClassName;
    
          default:
            return undefined;
        }
      };
    
      return (
        <BaseNavLink
          className={(props) =>
            [
              getCustomClassName(props), // NavLink "prop"
              className                  // styled-component "prop"
            ]
              .filter(Boolean)
              .join(" ")
          }
          {...props}
        />
      );
    };
    
    export const StyledNavLink = styled(NavLink)<NavLinkProps>``;
    

    The UI using the StyledNavLink component will use an customClassName prop instead of className and pass the same function or string value.

    function App() {
      return (
        <HashRouter>
          <nav>
            <StyledNavLink
              to="/home"
              customClassName={({ isActive }) => (isActive ? "current" : "")}
            >
              Home
            </StyledNavLink>
            <StyledNavLink
              to="/about"
              customClassName={({ isActive }) => (isActive ? "current" : "")}
            >
              About Us
            </StyledNavLink>
            <StyledNavLink
              customClassName="current" // <-- always applied
              to="/contact"
            >
              Contact
            </StyledNavLink>
          </nav>
          <Routes>
            <Route path="/" element={<Home />} />
            <Route path="/home" element={<Home />} />
            <Route path="/about" element={<About />} />
            <Route path="/contact" element={<ContactUs />} />
          </Routes>
        </HashRouter>
      );
    }
    

    Edit using-navlink-with-styled-component-in-react-router-dom-6-is-adding-function-to

    enter image description here

    Login or Signup to reply.
Please signup or login to give your own answer.
Back To Top
Search