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.
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
Next, I will add imports for
@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
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
Tab with label favorites with associated value of
books. 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
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
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
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
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 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
If we take a look at the API documentation for
react-router-dom, we can see that it has a prop called
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
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
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!