The Complete Guide to MUI IconButton onClick and Hover

The Material-UI IconButton creates a clickable icon with all the props and behavior of a MUI Button. In this post we will learn how to add hover functionality such as opening a tooltip and changing color. We will also add an onClick handler and examine how IconButtons are rendered in the DOM.

Before we examine this code, I’ll dive into the differences between MUI Button and IconButton and the classes that impact their styling differences.

A Code Sandbox link to a live example is in the Resources section.

Material-UI Button vs. IconButton

The MUI IconButton is a special kind of Button component. It accepts all the props of the MUI ButtonBase.

Visually, there are two primary differences in the IconButton and Button. First, the IconButton is often rendered without any text (innerText in HTML). This tends to reduce it’s width and makes it more commonly rendered in a circular shape instead of a rectangle.

Second, the IconButton (on the right below) is rendered by default with MUIIconButton-root class instead of MUIButton-container (or any other variant class). Notably, MUIIconButton-root sets border radius to 50% and sets background color to transparent.

MUI Button vs MUI IconButton
MUI Button vs MUI IconButton in the DOM

In the screenshot above, the Button component on the left and the IconButton on the right both are making use of the MUI SaveIcon. The Button is receiving it as a prop: endIcon={<SaveIcon />}. The IconButton is receiving it as a child component. This example shows there is not much difference in the two types of buttons.

Here’s how to set background color, size, and more on the IconButton.

Material-UI IconButton onClick with TypeScript

Adding an onClick handler to the IconButton is the same as adding it to most components, including a regular MUI Button. The TypeScript typing is the same as with the Button.

//handler
const handleClick = (event: React.MouseEvent<HTMLButtonElement>) => {
  console.log(event.target);
};

//JSX in return statement
<IconButton
  onClick={handleClick}
>
  <SaveIcon />
</IconButton>

In the above code, the event is typed as a React MouseEvent. We can then access the target and see precisely what was clicked. Since the onClick was at the IconButton level, the target might be the button element or the svg element.

This handler is a very simple example. However, having access to the event is powerful and can be used for a variety of use cases.

Material-UI IconButton Hover

When you hover over an IconButton, you usually want to do one of two things:

  • Fire an event and run some JavaScript in the handler
  • Add hover styling using CSS

We will look at examples of both of these below. As info, hover styling can be applied using a handler like onMouseOver, but it requires less code and runs faster to add hover style using CSS.

If you simply want to remove default hover styling on a Material-UI IconButton, add the following code:

<IconButton
  sx={{
    "&:hover": {
      backgroundColor: "transparent",
      cursor: "default"
      }
  }}
>
  <SaveIcon />
//...
</IconButton>

This reverses the two default hover stylings that MUI adds. You may also want to set disableRipple={true} so to remove the ripple effect that occurs on click.

Material-UI IconButton ToolTip on Hover

Material-UI makes it remarkably easy to add a tooltip on button hover. Take a look at the abbreviated code below:

<Tooltip title="Save">
  <IconButton>
    <SaveIcon />
  </IconButton>
</Tooltip>

Simply wrap the IconButton in a Tooltip and give the Tooltip a title (which is the text it will display). MUI then adds the desired hover functionality to the IconButton.

In my example, I have a tooltip but I also have default hover styling disabled. In practice you likely want hover styling if you have a tooltip.

The Tooltip is extremely customizable with over twenty props. Most of these are focused on positioning, Tooltip interactivity, and styling.

Material-UI IconButton Hover Color

We saw earlier how to remove default hover styling. Adding hover color is similar:

<IconButton
  sx={{ "&:hover": { color: "green" } }}
>
  <SaveIcon />
</IconButton>

The import bits of syntax for hover styling are below:

  • Add the hover styling to the sx prop with a nested selector.
  • Make sure the nested selector selects the button element, not a child element (no space between the ampersand and the colon)
  • Make sure the styling is added at the IconButton level, not the child icon level.

In the DOM this selector fires when the <button> element is hovered over. This means it will fire when even the IconButton padding is hovered and the color of the child icon will change.

Material-UI Button With Icon Text Change on Hover

Changing MUI Button text on hover (onMouseOver) and then reverting the text onMouseOut is quite challenging when an Icon is involved.

Text can be added to a button on hover using CSS. Take a look at the &:hover:before selector which added the text “delete”. We could also add &:before to revert the text when hover ended. However, it is difficult to add and remove the HTML for the icon, especially if we want to play nicely with MUI.

Instead, I used the onMouseOver and onMouseOut events and React’s useRef hook. Handling these events gave me the ability to use JavaScript and drill into the ref (the reference to the element).

I needed access to the innerHTML, and I also needed a way to ‘remember’ the original SaveIcon HTML. I accomplished this by creating an innerHTMLSaver field on the ref, where I stored the HTML before changing it.

//TypeScript typing for ref:
const ref = React.useRef<HTMLElement | null>(null);

//return statement
<Button
  ref={ref}
  variant="contained"
  sx={{
    //"&:hover:before": { content: `"delete"` }
    width: 100 //necessary for replacing text
  }}
  endIcon={<SaveIcon />}
  onMouseOver={() => {
    ref.current.innerHTMLSaver = ref.current.innerHTML;
    ref.current.innerText = "TEST";
  }}
  onMouseOut={() => {
    ref.current.innerHTML = ref.current.innerHTMLSaver;
  }}
>
  Save
</Button>

Even though this was technically feasible, I don’t recommend doing it. One significant issue was the button size changing when the width of the rendered innerHTML changed. This would mean the button ‘jumped’ and sometimes the onMouseOut event did not trigger. That’s why I set a fixed width on the button. Even still, I noticed odd behavior and simply don’t recommend changing the text on an IconButton or a Button with an Icon inside.

How to Create a Clickable MUI Icon

You can create a clickable icon in MUI without a wrapping IconButton. Here’s the code using the Save icon:

<SaveIcon color="primary" onClick={()=>{console.log('Clicked!')}} role="button" tabIndex={0} />

You might want to create a clickable icon without a wrapping button if you don’t need the default IconButton styling. In the previous example where I added a Tooltip to the IconButton, I removed the ripple. This left no visual difference between an icon wrapped with an IconButton, and a solitary Icon.

The one on the left is wrapped in an IconButton, the right is not.

Clickable MUI Icon
Clickable MUI Icon

The other benefit besides styling that IconButtons provide is semantic meaning for browsers, search engines, and accessibility tools. If you create a clickable Icon that is not inside a button, I suggest adding a role and tabIndex as suggested by the ESLint jsx-a11y plugin.

MUI Icon onClick With TypeScript

Here is the TypeScript typing for the MUI Icon click handler:

const handleClick = (event: React.MouseEvent<SVGSVGElement>) => {
  //do something
};

The handleClick function would be passed to the MUI Icon’s onClick prop.

This is almost the same typing as the IconButton onClick, but it receives a SVGSVGElement (not a typo, that’s the name). This is because the Icon renders as an svg in the DOM.

Resources

Here are useful related posts:

MUI IconButton API

Code Sandbox

Share this post:

Leave a Comment

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