In my React <Header>
component I use Material UI Tabs to create a primary menu for the main navigation, with react router links instead of showing tab content:
<Tabs value={location.pathname}>
{(items || []).map((item) => (
<Tab
key={item.url}
label={item.title}
value={item.url}
href={item.url}
disableRipple
/>
))}
</Tabs>
On the same page I use Material UI Tabs again for a secondary menu.
The route path to the main navigation menu item is: /example-path
. When I navigate to this route, this menu items is highlighted and has active state.
When the tab of secondary menu item with the same route as the highlighted primary menu item /example-path
, both menu items gets highlighted and active state.
When I click on another secondary menu item only this secondary menu item gets highlighted /example-path/tab-two
.
How do I manage both gets highlighted in the same time, The parent from main navigation and the various menu items from the secondary menu?
The Header
component should match on only the root path segment while the Submenu
component can compare the complete URL pathname
. Note that if you've further sub-routes then Submenu
would need to check the first two segments.
Instead of the Tab
components rendering a raw anchor tag <a>
it should render a Link
component. This is so the router can respond to and handle the navigation action instead of letting the browser make a page request to the server and reload the entire app.
Header
import { Tabs, Tab } from "@material-ui/core";
import { Link, useLocation } from "react-router-dom";
const Header = () => {
const { pathname } = useLocation();
const base = `/${pathname.slice(1).split("/").shift()}`;
return (
<Tabs value={base}>
<Tab component={Link} to="/" label="Home" key="/" value="/" />
<Tab
component={Link}
to="/about"
label="About"
key="/about"
value="/about"
/>
<Tab
component={Link}
to="/dashboard"
label="Dashboard"
key="/dashboard"
value="/dashboard"
/>
</Tabs>
);
};
export default Header;
Submenu
import { Tabs, Tab } from "@material-ui/core";
import { Link, useLocation } from "react-router-dom";
const Submenu = () => {
const { pathname } = useLocation();
return (
<Tabs value={pathname}>
<Tab
component={Link}
to="/about/about-one"
label="About sub one"
key="1"
value="/about/about-one"
/>
<Tab
component={Link}
to="/about/about-two"
label="About sub two"
key="2"
value="/about/about-two"
/>
</Tabs>
);
};
export default Submenu;
App
Order the routes within the Switch
in inverse order of path specificity so that more specific paths are matched before falling back to less specific paths.
<Router>
<Layout>
<Switch>
<Route path="/about/about-one">
<AboutOne />
</Route>
<Route path="/about/about-two">
<AboutTwo />
</Route>
<Route path="/about">
<About />
</Route>
<Route path="/dashboard">
<Dashboard />
</Route>
<Route path="/">
<Home />
</Route>
</Switch>
</Layout>
</Router>
Update to handle submenu tabs on the root "/"
route. For this you'll need to do a bit of a "hack" to check that the URL path is not one of the other main root routes, so that you can override the base Tabs
value to be exactly "/"
instead of one of the "/home-section/*"
sub-routes, using the matchPath
utility function.
Example:
import { Tabs, Tab } from "@material-ui/core";
import { Link, useLocation, matchPath } from "react-router-dom";
const Header = () => {
const { pathname } = useLocation();
const base = `/${pathname.slice(1).split("/").shift()}`;
const isNotHome = ["/about", "/dashboard"].some((path) =>
matchPath(pathname, path)
);
return (
<Tabs value={isNotHome ? base : "/"}>
<Tab component={Link} to="/" label="Home" key="1" value="/" />
<Tab component={Link} to="/about" label="About" key="2" value="/about" />
<Tab
component={Link}
to="/dashboard"
label="Dashboard"
key="3"
value="/dashboard"
/>
</Tabs>
);
};
export default Header;