'Material UI: Display sub-element on hover of parent

When the user hovers over a Card component, I'd like to show a button on that component that is otherwise invisible. In CSS, I'd do something like this:

.card:hover my-button {
  display: block;
}

How do I replicate this in the "Material-UI" way?

All the Material-UI tips I found so far suggest something like this to add hover styling, but this applies the styles to the component that is being hovered over and not a different one.

  '&:hover': {
    background: 'blue'
  }


Solution 1:[1]

You can do the exact same thing with CSS using the createMuiTheme:

export const theme = createMuiTheme({
  overrides: {
    // For label
    MuiCard: {
      root: {
        "& .hidden-button": {
          display: "none"
        },
        "&:hover .hidden-button": {
          display: "flex"
        }
      }
    }
  }
});

Give the Button inside your Card the className hidden-button and you will get the same thing that you want.

Check it here: https://codesandbox.io/s/mui-theme-css-hover-example-n8ou5

Solution 2:[2]

It is not specific to Material UI but a react specific thing. you need a state variable to show/hide your button.

const App = () => {
  const [show, setShow] = useState(false);
  return (
    <Card
      onMouseOver={() => setShow(true)}
      onMouseOut={() => setShow(false)}>
      <CardBody>
        // some content
        {show && <Button>Click</Button>}
      </CardBody>
    </Card>
  );

}

Solution 3:[3]

import {
  makeStyles
} from '@material-ui/core'

const useStyles = makeStyles(() => ({
  root: {
    "& .appear-item": {
      display: "none"
    },
    "&:hover .appear-item": {
      display: "block"
    }
  }
}))

export default function MakeTextAppearOnHover() {
  const classes = useStyles()
  return (
    <div className = { classes.root }>
      Hello world
      <span className = 'appear-item' >
        Appearing text Go
      </span>
    </div>
  )
}

Solution 4:[4]

This is a material UI example that displays the sub-element on hover of the parent. I also noticed that using some of the examples above, when using Material UI's makeStyles, the overlay item was flickering a lot when clicked. This solution below does not flicker when sub-element is clicked.

import React from "react"
import { Card, CardActionArea, CardContent, CardMedia } from "@material- 
       ui/core";
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles(theme => ({
    card: {
        // some styles
    },
    cardAction: {
        position: "relative"
    },
    media: {
        // some styles
    },
    overlay: {
        position: "absolute",
        top: "85px"
    }
}));

const App = () => {
    const classes = useStyles();
    const [show, setShow] = React.useState(false);
    const handleMouseOver = () => {
        setShow(true);
    };
    const handleMouseOut = () => {
        setShow(false);
    };
    return (
      <Card>
          <CardActionArea
          onMouseOver={handleMouseOver}
          onMouseOut={handleMouseOut} className={classes.card} >
              <CardMedia className={classes.media} component="img" >
              // some content
              </CardMedia>
              <CardContent className={classes.overlay} style={{ display: show ? 
               'block' : 'none' }>
               // some content
              </CardContent>
           </CardActionArea>
      </Card>
  );
}

Solution 5:[5]

If you want to define this purely inside of a styled component instead of using either of createMuiTheme or makeStyles, then try the following.
We will give an id to each child component and reference them when defining the behaviour to implement when we hover over the parent component:

const NameCellBox = styled(Box)(({ theme }) => ({
    ...other styles,
    "&:hover #cellBoxLengthTypo": {
        display: "none",
    },
    "&:hover #cellBoxContentTypo": {
        display: "inline-block",
    },
}));

const CellBoxLengthTypo = styled(Typography)(({ theme }) => ({
    fontFamily: theme.typography.fontFamily,
    fontSize: theme.typography.h5.fontSize,
}));

const CellBoxContentTypo = styled(Typography)(({ theme }) => ({
    display: "none",
    fontFamily: theme.typography.fontFamily,
    fontSize: theme.typography.h5.fontSize,
}));
const items: string[] = ["andrew", "barry", "chris", "debbie"];
return (
    <>
        <NameCellBox>
            <CellBoxLengthTypo id="cellBoxLengthTypo">+{items.length}</CellBoxLengthTypo>
            <CellBoxContentTypo id="cellBoxContentTypo">{items.join(", ")}</CellBoxContentTypo>
        </NameCellBox>
    </>
);

Found it useful for updating a DataGrid cell in Material UI when hover or other event fired.

Sources

This article follows the attribution requirements of Stack Overflow and is licensed under CC BY-SA 3.0.

Source: Stack Overflow

Solution Source
Solution 1 Dekel
Solution 2 Zohaib Ijaz
Solution 3
Solution 4 Dotun
Solution 5 Oisín Foley