# Snippet: Material UI Responsive Icon Button

From time to time, I find myself needing a Material UI `Button` with an icon and label that collapses the label text down on smaller screens, essentially turning it into an `IconButton`. I've written various approaches to this several times and decided it was time to post the code for the next time I need to find it.

## Goals:

* The `Button` label text hides below a certain MUI breakpoint
    
* Target breakpoint can be passed as a prop with a sane default (`'sm'`) but not spread onto the button component
    
* Utilizes MUI styled components with props
    
* Utilizes TypeScript and uses MUI `Breakpoints` type
    
* For my purposes, default the `Button` to `variant="outlined"` and `color="primary"` tweak some default styles.
    

## The Code

```typescript
// Responsive Icon Button
import React from 'react';
import Button, { ButtonProps } from '@mui/material/Button';
import { styled } from '@mui/material/styles';
import { Breakpoint } from '@mui/system/createTheme/createBreakpoints';

interface ResponsiveIconButtonProps extends ButtonProps {
  breakpoint: Breakpoint;
}

const ResponsiveIconButtonStyled = styled(Button, {
  shouldForwardProp: (prop) => prop != 'breakpoint',
})<ResponsiveIconButtonProps>(({ theme, breakpoint }) => ({
  fontSize: theme.typography.pxToRem(14),
  minWidth: 'auto',

  [theme.breakpoints.down(breakpoint)]: {
    minWidth: 32,
    paddingLeft: 8,
    paddingRight: 8,
    '& .MuiButton-startIcon': {
      margin: 0,
    },
    '& .buttonText': {
      display: 'none',
    },
  },
}));

export default function ResponsiveIconButton(props: ResponsiveIconButtonProps): JSX.Element {
  const { children, ...rest } = props;

  return (
    <ResponsiveIconButtonStyled variant="outlined" color="primary" {...rest}>
      <span className="buttonText">{children}</span>
    </ResponsiveIconButtonStyled>
  );
}

ResponsiveIconButton.defaultProps = {
  breakpoint: 'sm',
};
```

The below image shows the final component in action at two different breakpoints.

![](https://cdn.hashnode.com/res/hashnode/image/upload/v1676995516170/a68236ba-b839-436b-907b-ace89b17b9c4.png align="center")

## But Why?

Material UI is extremely flexible and I love working with it. However, when attempting to hide button text/label without any additional markup, I couldn't get an "edge" in CSS to select specifically the text. Below you can see a modified version of the rendered HTML from the Material UI `Button` component with an `endIcon`. Note that in the rendered output the label "Send" doesn't have a wrapper element.

```typescript
// React Code
<Button variant="contained" endIcon={<SendIcon />}>
  Send
</Button>

// Rendered markup (with classes removed)
<button>
  Send
  <span>
    <svg><path d="M2.01 21 23 12 2.01 3 2 10l15 2-15 2z"></path></svg>  
  </span>
</button>
```

Also, in previous attempts at this, I hardcoded the breakpoints, which felt silly. Utilizing the `Breakpoint` type from MUI makes for nice auto-completion in VScode.

![Graphic showing breakpoints autocompleting](https://cdn.hashnode.com/res/hashnode/image/upload/v1676995271894/033c65ef-0cb8-4e95-ac14-e3fa3be91dce.png align="center")

## Notes:

* The above code was tested in MUI 5.10.1
    
* Thanks to Ryan Cogswell for proposing this basic solution to me [on Stack Overflow](https://stackoverflow.com/questions/61352423/what-is-best-approach-to-responsive-buttons-with-icons-in-material-ui) in 2020.
