skip to Main Content

In a dashboard layout, let’s say I have a sidebar that contains the option ‘Leave’. When I click on leave, leave gets highlighted on the sidebar, and the leave component is displayed. In the leave component, there is a button ‘Apply Leave’. When I click on ‘Apply Leave’, the Apply Leave Component containing the form is displayed, but ‘Leave’ on sidebar is not highlighted anymore.

Also I want to make the sidebar scroll independently of the browser scrolling and show the scrollbars only while scrolling. Just like youtube sidebar which scrolls seperately from the video feed scrollbar.

Here is my code:

Sidebar.js

import { useState, useEffect } from "react";
import { useLocation } from "react-router-dom";
import "../assets/styles/Sidebar.css";
import SidebarItem from "./SidebarItem";

function Sidebar() {
  const location = useLocation();
  const [selectedItem, setSelectedItem] = useState("");

  useEffect(() => {
    const pathname = location.pathname;
    const item = getItemFromPathname(pathname);
    setSelectedItem(item);
  }, [location]);

  const getItemFromPathname = (pathname) => {
    const parts = pathname.split("/");
    const lastPart = parts[parts.length - 1];
    switch (lastPart) {
      case "":
      case "profile":
        return "Profile";
      case "calendar":
        return "Calendar";
      case "leave":
        return "Leave";
      default:
        return "";
    }
  };

  const handleItemClick = (title) => {
    setSelectedItem(title);
  };

  return (
    <section className="sidebar">
      <SidebarItem
        to="/"
        title="Profile"
        selected={selectedItem === "Profile"}
        onClick={() => handleItemClick("Profile")}
      />
      <SidebarItem
        to="/calendar"
        title="Calendar"
        selected={selectedItem === "Calendar"}
        onClick={() => handleItemClick("Calendar")}
      />
      <SidebarItem
        to="/leave"
        title="Leave"
        selected={selectedItem === "Leave"}
        onClick={() => handleItemClick("Leave")}
      />
    </section>
  );
}

export default Sidebar;

SidebarItem.js

import { Link } from "react-router-dom";
import "../assets/styles/SidebarItem.css"

function SidebarItem(props) {
  return (
    <Link to={props.to} className="sidebar-item-link">
      <div
        className={`sidebar-item ${props.selected && "selected"}`} 
        onClick={props.onClick}
      >
        {props.title}
      </div>
    </Link>
  );
}

export default SidebarItem;

SidebarItem.css

.sidebar-item.selected {
  border-left: 10px solid #63809f;
  font-weight: bold;
}

2

Answers


  1. you can create a function to check if you are in the route (or subroute) of selected sidebar button.

    maybe something like this:

    function isInRoute(
      // all routes you want to check
      routes: string[],
      // router manager where you can get the current route/subroute
      router: DEFINE_TYPE_HERE,
      // must match the route - usefull with root route (/) 
      // or if must match a subroute (some/thing)
      exactly?: boolean
    ): boolean {
      if ( !routes || routes.length <= 0 ) return false
      if ( !router ) return false
    
      const { pathname } = router
      return routes.some( ( route ) => {
        if ( !route || route.trim().length <= 0 ) return false
    
        /* eslint-disable no-useless-escape */
        const regex = new RegExp(
          // if you don't pass the exactly prop but want to check
          // if you are in root (/) route, you can use it as '/'
          route === '/' || exactly ?
            `^${ route }$` : `${ route }`
        )
        return regex.test( pathname )
      } )
    }
    

    then use it like this:

    // current route: '/user/xx'
    
    // returns false because you are in a subrouter of /user
    isInRoute(
      ['user'],
      YOUR_ROUTER,
      true
    ) 
    
    // returns true because the root route is /user
    isInRoute(
      ['user'],
      YOUR_ROUTER
    ) 
    
    

    hope this helps

    PS: if you are using JS instead of TS just remove types in function params

    Login or Signup to reply.
  2. You might just need to switch over to using the NavLink component which includes route path matching functionality already, no need to re-invent the wheel.

    Example:

    import { NavLink } from "react-router-dom";
    import "../assets/styles/SidebarItem.css"
    
    function SidebarItem({ title, ...props }) {
      return (
        <NavLink className="sidebar-item-link" {...props}>
          {({ isActive }) => (
            <div className={`sidebar-item ${isActive && "selected"}`}>{title}</div>
          )}
        </NavLink>
      );
    }
    
    export default SidebarItem;
    

    When the current URL path is "/leave/*" (or "/calendar/*", etc) the sidebar item will be active, or "selected".

    There’s likely also no need for the selectedItem state in the parent component now since selected/active state is computed by react-router-dom and the NavLink component.

    function Sidebar() {
      return (
        <section className="sidebar">
          <SidebarItem to="/" title="Profile" end /> // *
          <SidebarItem to="/calendar" title="Calendar" />
          <SidebarItem to="/leave" title="Leave" />
        </section>
      );
    }
    
    export default Sidebar;
    

    Edit how-to-highlight-sidebar-items-even-when-you-are-inside-a-submenu-of-a-particula

    * Note: The end prop is used specifically for "/" so it’s not also matched with other sub-routes. The alternative here would be to give the Profile page its own non-"/" path, e.g. "/profile".

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