How to Style Material-UI Tabs and Tab Indicator (Plus TypeScript)

Material-UI Tabs are a handy component for easy navigation with out-of-the-box styling. However, sometimes that styling is more than we need.

In this Tab Panel example, we will disable the indicator (the underline at the bottom of the Tab just above the shadow) and instead add our own styling. We will style Material-UI’s Tabs with custom background color, font size and padding.

You will also see how to change the tab indicator color in case you prefer that to display: none.

Finally, I use TypeScript in my example. The trickiest TypeScript code was the Tab onChange, which we’ll discuss.

Code Sandbox with full React code is in the Resources section. It is a fork of the demo in the docs.

Material-UI Tab Indicator Styling

Take a look at the screenshot below of the DOM elements generated by Material-UI. The first three are the three tabs. The fourth, a <span> element, is the tab indicator.

Material-UI Tab Indicator in the DOM

MUI by default applied the class MuiTabs-indicator to the element. This will give us a handy option for using CSS selectors to target the element.

//useStyles
tabs: {

  "& .MuiTabs-indicator": {
    display: "none"
    //backgroundColor: "orange"
  }

}


//JSX snippet in return
<Tabs
  variant="fullWidth"
  value={value}
  onChange={handleChange}

  className={classes.tabs}
>

In my tabs class, I targeted the MuiTabs-indicator. The space between the & and the global class name indicates it is selecting a child element of whatever parent has tabs class applied.

A simple display: none does the trick of hiding the indicator. If you want to change the indicator color instead, simply apply a background color in this selector.

There are likely quite a few customizations you could do here to the tab indicator size, padding, margin, or anything else interesting.

Material-UI Tab and TabPanel Styling

I used two different approaches to customizing the tabs simply to show what is possible. The first is to use a custom theme color and let the tabs use that for their background color. The second is to give individual tabs a class and then properly select the MUI global classes on the tab.

Below is the code for the custom theme:

const customTheme = createMuiTheme({
  palette: {
    primary: {
      light: "#42c2f5",
      main: "rgba(0,0,0,0.5)",
      dark: "#778899",
      contrastText: "#fff"
    }
  }
});

//JSX snippet in return
<ThemeProvider theme={customTheme}>
  <div className={classes.root}>
    <AppBar position="static" elevation={9}>
      <Tabs>
        //children components here

The tabs take their background color from the theme.palette.primary.main color. Set this color in a custom theme and pass it in using ThemeProvider. In the Code Sandbox demo, this is only applied to the first tab, I used individual classes for tabs two and three.

Let’s take a look at the code for individual classes:

tabTwo: {
  "&.MuiButtonBase-root.MuiTab-root": {
    backgroundColor: "green"
  }
},
tabThree: {
  "&.MuiButtonBase-root.MuiTab-root": {
    backgroundColor: "yellow"
  }
}

//JSX snippet in return
<LinkTab
  label="Page Two"
  href="/trash"
  className={classes.tabTwo}
/>
<LinkTab
  label="Page Three"
  href="/spam"
  className={classes.tabThree}
/>

LinkTab is simply a wrapper around Tab for using href.

MUI applies lots of default classes to the Tab components. This makes it challenging to get our styling to apply. In fact, I had to select two existing classes that are at the same level in the DOM as the tabTwo and tabThree classes: &.MuiButtonBase-root.MuiTab-root.

Inside of these I set the Tab background color. I could also have applied font size, color, and more at this level. Instead I one font size to all tabs. I also gave all tabs an underline when selected:

tabs: {
  "& .MuiButtonBase-root.MuiTab-root": {
    fontSize: 20
  },

  "& .Mui-selected": {
    textDecoration: "underline"
  }
}

These selectors are in addition to the indicator selector shown above.

I set fontSize on all elements with classes .MuiButtonBase-root.MuiTab-root that are children of elements with tabs class. This will set all tabs in our demo with fontSize 20.

Mui-selected is a class that gets automatically applied by Material-UI when a tab is selected. I used it to add an underline to the text. This is in a different position than the underline visible via the indicator.

The TabPanel is a wrapper over whatever we want to display when a tab is selected. In our demo, and in the original demo from the docs, the TabPanels are below the AppBar (which contains the Tabs) and the appropriate TabPanel is visible when it’s relevant Tab is selected.

I simply added a class to each TabPanel to give them padding and background color:

tabPanel: {
  backgroundColor: "rgba(1,1,1,0.1)",
  paddingTop: 12
}

//JSX snippet in return
<TabPanel className={classes.tabPanel} value={value} index={0}>
  Page One
</TabPanel>
<TabPanel className={classes.tabPanel} value={value} index={1}>
  Page Two
</TabPanel>
<TabPanel className={classes.tabPanel} value={value} index={2}>
  Page Three
</TabPanel>

Material-UI Tabs With TypeScript

Material-UI Types are included in the material-ui/core library, so is no additional dependency needed to use TypeScript with MUI.

The most difficult typing is the typing for the onChange handler.

const handleChange = (
  event: React.ChangeEvent<{}>, newValue: number
) => {
       setValue(newValue);
     };

There’s nothing uniquely Material-UI in this typing. Finding the correct type for any handler in React with TypeScript can be a challenge.

Other than the above, most TypeScript typing in the demo is interfaces for props.

Resources

Thanks to this SO answer that pointed out that types are bundled with MUI.

Want to test your JavaScript knowledge? Check out these 50 difficult JS questions.

Code Sandbox:

Share this post:

Leave a Comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.