Material-UI Tabs are composed of several components: the Tabs component, child Tab components, and usually a container and tab panel. We can quickly add perfect styling to these elements with the sx
prop and some nested selectors.
Additionally, I will show the difference in Tab onClick
, Tabs onChange
, and which one is best to use.

The Resources section has a link to a live Code Sandbox demo. A YouTube version of this post is here.
Here are additional useful links:
- Complete guides to MUI ‘sx’ prop, styled components, and makeStyles
- How to Style Material-UI Tabs
- The Ultimate MUI Tab Indicator Tutorial (Props, Color, Size)
- How to select pseudo elements and pseudo classes in MUI
- The Best MUI Accordion onClick, Expand, and Icons Tutorial
MUI Tab Hover, Active, and Focus Color
The two components we are most interested in are MUI Tabs and individual MUI Tab components (it’s confusing!). The Tabs component is a wrapper that renders three divs deep to handle horizontal scrolling. Each Tab renders as a single button element that wraps a span.

It is important to avoid redundant styling code on each Tab. Instead, we could create a styled Tabs component or add selectors to the Tabs sx
prop that target all Tab buttons. Either is a fine choice; I added selectors to the Tabs sx
in the code below.
<Tabs
value={value}
onChange={handleChange}
sx={{
"& button": { borderRadius: 2 },
"& button:hover": { backgroundColor: "blue" },
"& button:focus": { backgroundColor: "yellow" },
"& button:active": { backgroundColor: "green" }
}}
>
<Tab label="Item One" />
<Tab label="Item Two" />
<Tab label="Item Three" />
</Tabs>
These selectors show why it is important to know how MUI renders in the DOM. Without exploring the DOM, we might not know that a Tab renders as a button.
I first selected all buttons within the Tabs component and gave them a border radius. There is no default styling on a Tab so this is invisible until the Tab is selected.
Notice the syntax for Tab hover. The empty space after &
means that it selects a child element. The button:hover
syntax is the pseudo selector syntax.
The Tab active and focus syntax is the same as the hover syntax because they are also pseudo classes applied by the browser when the relevant pointer event occurs.
The focus pseudo class is applied by the browser when the Tab gets focus. This can occur by a tab keyboard event or by a mouse click. The active pseudo class is applied immediately when the user clicks down (mouse down event) on the tab, and stays applied until the click is let up (mouse up).
In my example code, the focus
selector is above the active
selector. This means in the brief moment during a click when both active
and focus
are applied, active
styling will override focus styling. Code order matters!
After mouse up occurs, the Tab will still have focus
until a new element gets focus. However, the Mui-selected class is applied be default on click of the tab and remains on the tab. Mui-selected can be used to style the selected tab after the focus class is no longer applied by the browser.
MUI Tab onClick vs Tabs onChange (With TypeScript)
The Tab onClick event passes only one value: the DOM event. It is possible to dig into this parameter and extract a useful value, but instead I recommend using the Tabs onChange handler. This passes the DOM event and a tab index value.

In the example code below you can see the TypeScript typing of the ChangeEvent and MouseEvent (the click event). The value
is a state value for controlling which tab panel is visible.
const [value, setValue] = React.useState(0);
const handleChange = (event: React.ChangeEvent<{}>, newValue: number) => {
setValue(newValue);
console.log(`value: ${newValue}`);
};
const handleClick = (event: React.MouseEvent) => {
console.log(event);
};
//JSX
<Tabs
value={value}
onChange={handleChange}
>
<Tab onClick={handleClick} />
<Tab onClick={handleClick} />
<Tab onClick={handleClick}/>
</Tabs>