39

I have a web app that I'v designed with material-UI and as you can see below I'm using Button navigation for navigating through my basic landing page components.

<div className="footer">
  <BottomNavigation value={value} onChange={this.handleChange} className={classes.root}>
    <BottomNavigationAction label="signal" value="signal" icon={<ShowChart />} className={classes.content}/>
    <BottomNavigationAction label="hotlist" value="hotlist" icon={<HotList />} className={classes.content}/>
    <BottomNavigationAction label="analyze" value="analyze" icon={<PieChart />} className={classes.content}/>
    <BottomNavigationAction label="learn" value="learn" icon={<LibraryBooks/>} className={classes.content}/>
    <BottomNavigationAction label="dashboard" value="dashboard" icon={<PermIdentity/>} className={classes.content}/>
  </BottomNavigation>
  </div>

I've tried to use React-Router with these predefiend navigation component but that didn't work, is there any possible way to use Router with Button navigation of material-UI? Button navigation article in material-UI ButtonNavigation API

El.
  • 1,217
  • 1
  • 13
  • 23

4 Answers4

74

Yes, it's possible. You need to use the component prop:

import { Link } from 'react-router-dom';

import BottomNavigation from '@material-ui/core/BottomNavigation';
import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';

// ....

<BottomNavigation value={value} onChange={this.handleChange}>
    <BottomNavigationAction
        component={Link}
        to="/signal"
        label="signal"
        value="signal"
        icon={<ShowChart />}
        className={classes.content}
    />
</BottomNavigation>

(the to prop is for React Router's Link component)

This works with any Material-UI component that inherits from ButtonBase.

https://material-ui.com/api/bottom-navigation-action/

Inheritance

The properties of the ButtonBase component are also available. You can take advantage of this behavior to target nested components.

https://material-ui.com/api/button-base/

Props

component - The component used for the root node. Either a string to use a DOM element or a component.

thirtydot
  • 224,678
  • 48
  • 389
  • 349
  • 4
    If you need BottomNavigationAction to be shown as selected when you visit an url, you need to 1. get current location using `useLocation` from `react-router-dom` 2. give it to `BottomNavigation` with `value={location.pathname}` 3. give each `BottomNavigationAction` proper `value` prop, e.g. `value="/about" – trashgenerator Aug 19 '21 at 07:17
10

Just to add on to the great answer by @thirtydot , in case the user types into the search and visits a particular webpage directly (other than default) e.g. "www.yoursite.com/tab2", instead of clicking the 2nd button, this may cause a mismatch between the site that is showing and the BottomNav Button that is focused (usually the 1st button).

Here is what I did:
I used window.location.pathname to get the current path which is '/tab2' directly.
Here is my code for my particular use case....

function BottomNavBar(){
    const pathname = window.location.pathname; // in case user visits the path directly. The BottomNavBar is able to follow suit.
    const [value, setValue] = React.useState(pathname);
    const handleChange = (event, newValue) => {
      setValue(newValue);
    };

    return (
        <BottomNavigation value={value} onChange={handleChange} showLabels={true} >
          <BottomNavigationAction label="home" value="/" icon={<HomeIcon />} component={Link} to='/'/>
          <BottomNavigationAction label="resources" value="/resources" icon={<ResourcesIcon /> } component={Link} to='/resources'/>                
          <BottomNavigationAction label="Q&A" value="/qna" icon={<QnAIcon />}  component={Link} to='/qna'/>
          <BottomNavigationAction label="profile" value="/profile" icon={<ProfileIcon />} component={Link} to='/profile'/>
        </BottomNavigation>
      );
}

Daryll
  • 503
  • 1
  • 9
  • 15
  • 1
    Read the answer by @thirtydot first. – Daryll May 23 '20 at 12:42
  • @mcKindo No problem! Happy this helped – Daryll Apr 24 '21 at 17:10
  • 1
    An add on. Instead of using window.location.pathname (as you have to parse it.. think of the case if the url is '/tab2/123' instead), I believe for CRA users, most likely you will be using react-router-dom. You should use that instead to get the path. `import { useLocation } from 'react-router-dom'` or refer to this [other stackoverflow thread](https://stackoverflow.com/questions/42253277/react-router-v4-how-to-get-current-route) for other alternatives – Daryll Apr 24 '21 at 17:13
2

2023 complete answer

Building on @thirdydot's answer and @trashgenerator's comment, filling in all the remaining gaps.

If you want to hook up BottomNavigation with react-router-dom then you want the router to hold the single point of truth. This means to rely on useLocation instead of useState to store this aspect of your app's state and Link to change the state.

There's 3 steps to this:

  1. Make sure you are inside the router context for useLocation to work (see below).
  2. In BottomNavigation set value={useLocation().pathname} instead of useState and onChange.
  3. In BottomNavigationAction use component={Link} and to="/signal" and set the value to that same path.

src/components/RouterBottomNavigation.js

import { useLocation, Link } from 'react-router-dom';
import { BottomNavigation, BottomNavigationAction } from '@mui/material';
/* import icons */

export default function RouterBottomNavigation() {
  return (
    <BottomNavigation
      value={useLocation().pathname}
      className={classes.root}
    >
      <BottomNavigationAction
        component={Link}
        to="/signal"
        value="/signal"
        label="Signal"
        icon={<ShowChart />}
        className={classes.content}
      />
      <BottomNavigationAction
        component={Link}
        to="/dashboard"
        value="/dashboard"
        label="Dashboard"
        icon={<PermIdentity />}
        className={classes.content}
      />
    </BottomNavigation>
  )
}

How does this work? The location pathname now holds the state for your bottom navigation.

If it's /dashboard then your dashboard BottomNavigationAction is active, because it's value is also /dashboard.

And when you click a BottomNavigationAction that is also a router Link, you change the location pathname to the to value.

On being inside the router comntext

And to be inside of the router context, you could place the RouterBottomNavigation next to the Outlet. For example:

src/index.js

import React from 'react';
import ReactDOM from 'react-dom/client';
import { createBrowserRouter, RouterProvider } from "react-router-dom";
import Root from "./routes/root";
import Dashboard from "./routes/dashboard";
import Signal from "./routes/signal";

const router = createBrowserRouter([
  {
    path: "/",
    element: <Root />,
    children: [
      {
        path: "dashboard",
        element: <Dashboard />,
      },
      {
        path: "signal",
        element: <Signal />,
      },
    ]
  },
]);

const root = ReactDOM.createRoot(document.getElementById('root'));
root.render(
  <React.StrictMode>
    <RouterProvider router={router} />
  </React.StrictMode>
);

src/routes/root.js

import { Outlet } from 'react-router-dom';

import RouterBottomNavigation from "../components/RouterBottomNavigation";

export default function Root() {
  return (
    <>
      <Outlet />
      <RouterBottomNavigation />
    </>
  );
}
Jan
  • 3,044
  • 3
  • 20
  • 32
0

in new versions theres this property "LinkComponent" which lets you link directly to a component that renders the page you want, very similar to previus answers:

import { Link } from 'react-router-dom';

import BottomNavigation from '@material-ui/core/BottomNavigation';

import BottomNavigationAction from '@material-ui/core/BottomNavigationAction';

// ....

<div className="footer">
  <BottomNavigation value={value} onChange={this.handleChange} className={classes.root}>
    <BottomNavigationAction label="signal" value="signal" icon={<ShowChart />} className={classes.content} LinkComponent={Link} to={'/your-route'}/>
    <BottomNavigationAction label="hotlist" value="hotlist" icon={<HotList />} className={classes.content} LinkComponent={Link} to={'/your-route'}/>
    <BottomNavigationAction label="analyze" value="analyze" icon={<PieChart />} className={classes.content} LinkComponent={Link} to={'/your-route'}/>
    <BottomNavigationAction label="learn" value="learn" icon={<LibraryBooks/>} className={classes.content} LinkComponent={Link} to={'/your-route'}/>
    <BottomNavigationAction label="dashboard" value="dashboard" icon={<PermIdentity/>} className={classes.content} LinkComponent={Link} to={'/your-route'}/>
  </BottomNavigation>
  </div>
// ...