How to integrate Material-UI's Tabs with react router?

In this video, you will learn to integrate Tabs provided by Material-UI with the react-router. With that whenever you will click on any of the tabs, you will open a new route in the browser based on the tab you have clicked.

Full Transcript

Hello friends, its Harit from bonsaiilabs. In today's video, I will show you how to integrate Tabs provided by Material-UI with the react-router. With that whenever you will click on any of the tabs, you will open a new route in the browser based on the tab you have clicked. So, let's do it now!

I am here in codesandbox and will create a sandbox based on Official React template. Then, I will add the dependency on @material-ui/core.

Next, I will add imports for Tabs, and Tab from @material-ui/core. I will remove the implementation that comes default and add Tabs in place of that. Now, I will add 2 tab elements, one with label called Books, and another with the label of Favorites. And as we can see, both the tabs are available as the output on the right side.

Instead of showing the on the blank page, I will add them to the AppBar. For that, I will first import AppBar from material-ui/core. Then, I will wrap the Tabs element under AppBar. And with that, we see the tabs appeared inside the AppBar. But as I click on them, they are not going anywhere. Ideally, we want the routes for each one of these tabs and on click on them, the respective route should be loaded.

For that, I will start with adding a constant called routes which is an array with 2 entries, the first entry with /books and another entry as /favorites. And I will associate the value attribute of Tab element with these routes, where Tab with label books with associated value of books[0] and Tab with label favorites with associated value of books[1]. But even now, when I click on the elements, nothing happens, it's because the routes are not actual routes and no content is defined for these routes. I will do that work now.

I will create a new file called Books.js. This file is a simple stateless component, which renders "All Books" in h1 component when rendered. This is what we would want to see when we click on the Books Tab. Similarly, I will create a new file called Favorites.js. This file has similar implementation than before, just rendering a different title called "All Favorites" under h1 heading.

In the current state of app, even now clicking on Books, or Favorites Tab is not going anywhere. Also, going to /books is a blank page as well. This is because even though we have components that we want to render, we have not created routes in our application.

In order to do that, I will first import the dependency for react-router-dom. Next, I will import BrowserRouter component from react-router-dom. Then, I will wrap the entire implementation for with AppBar under BrowserRouter. The BrowserRouter uses the HTML5 history API to keep the UI in sync with URL. Next, I will create a router using Router component under BrowserRouter matching for the top-level root (/) URL, and wrap the AppBar inside it. I am doing this so that any URL we visit in this application, will first match this router and renter the AppBar at the top. You will soon see this when other routes start to work.

We now need 2 more routes, one for books and another one for favorites. At any time, we want one of these routes to be visible. We do not want the content for both of these routes available at the same time.

For that, I will use the Switch component from react-router-dom. With-in Switch, I will add 2 routes. The first route will be at path /books and when render will render the Books component that we added in a separate file. The second route will be available at /favorites and will display the Favorites component that we created few moments ago. With this change, if I click on the tabs nothing happened. However, if I now manually type the /books endpoint, I do see some content, but hidden under the AppBar component. Let me fix that. In the Books component file, I will add a style attribute to h1 component with marginTop property with a value of 15vh. And with that, we can see the content for Books under /books endpoint. Similarly, when I manually go to /favorites endpoint, I do see the component, but the content hidden under AppBar. I will apply the same style rules and we can confirm that the content from Favorites component is available under /favorites endpoint.

Fantastic! What's next?

The click of the tabs are still not taking to their corresponding endpoints. So let's connect these Material-UI components with the routes that we have created with react-router-dom. The Material-UI documentation has provided guidance on how to use it with routing libraries. It says to use component prop to define which Component to use, and a to prop to use to define the path when linking the Material-UI component with routing library such as react-router-dom. Let's make use of it in our case.

Back in the code, I will add a component prop to Tab and provide the Link component from react-router-dom. In this Tab, I want the /books route to be linked, so I will add routes[0] as the value. With that, when I go back to the app root and click on Books tab, we see that the /books route is loaded and the corresponding content from Books component is rendered. Great! We have now linked the react-router with Material-UI component. Let's do it for another tab as well. I will make a similar change to Favorites Tab, and with that we can smoothly go back and forth between the two routes with the click of a Tab provided by Material-UI.

But there seems to be a problem with our code. Let's look at the console. It says

` The value provided to the Tabs component is invalid ``

This is happening because the Tabs component expects a value prop whose value must match one of the underlying Tab's values. But in our case, we have not added the value prop at all. As I begin to add that, the question is - how do I know which value is active? Is it /books, or is it /favorites? Clearly, the answer depends on which URL path is active at this time. But how do I know that dynamically? In order to access that information, I need to have the access to history prop that is provided by react-router.

If we take a look at the API documentation for Route in react-router-dom, we can see that it has a prop called render. This render is a higher-order function that can display another component. The documentation also says that this prop function has access to all route props including the history prop that we are looking for. This means, if we change our top-level Route to render AppBar and access the value of path based on history props, we should be able to access the currently active path, which will serve as a value prop for Tabs component.

Let's try that!

Back in the code, I will first add a render prop to Route. Then, I will add a function as value, which renders our current implementation of AppBar. I will test current implementation to make sure that routes still work when the tabs are clicked. The console is still logging the invalid value for Tabs component, so let's fix that now. I will add history to the function, and add the value prop to Tabs component. I will add history.location.pathname to access the current path name. And with that, when I now click on any of the tabs, their corresponding paths are added to value prop of Tabs component. Also, as I navigate between the tabs, the current tab is highlighted visually. This is because the value is also added to one of the Tab components under the Tabs, and with a valid value prop Tabs knows to highlight them.

However, there is one final issue with this implementation. When I go to the root of the application, the problem re-surfaces. This is because the top-level path (/) is not among the valid values under any of the Tab under the Tabs component. We can fix this by checking if the path is not root (/), use that as the value for value prop, otherwise, add false as the value. Setting the value to false means that we do not want any Tab active. And with that, now when you click on any Tab, or even go to the root of the page, the error no longer appears.

Fantastic! I hope that now you can add confidently add routing to your material-ui components using what you have learned today in this video. If you like the content, consider subscribing us for more content, or tell us what difficulties you face when developing applications and we will cover them in our future videos.

Thank you for your time, and keep creating good things!