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!