2

I love the Material-UI library, but some things that I've found annoying:

  • I find Typography too long to write out, especially when I'm having to specify things like gutterBottom, align, etc. Whereas <H1> is much shorter, and I can always do manual overrides.
  • With Typography - I end up having to add a class to it anyway - if it is going to be contrastText against a primary background.
  • With buttons, I end up having to specify the 'contained' variant each time. Also, if I'm right aligning buttons with margin: "auto", and they contain an icon, then I also need to apply a display property to the button to get things to sit right, and then I'm repeating myself.
  • With buttons, if I want to change the default contained color (or non-primary/non-secondary), I have to do that manually.
  • With Cards, if my website has a common card style (ie. a certain elevation and padding) across the site, I have to repeat the elevation everywhere, or repeat references to that number.

Something I've considered is abstracting away Material-UI to a very simple wrapper library, with something like this.

function H1({ children, align = "center", ...rest }) {
    return (
        <Typography variant="h1" align={align} color="inherit" {...rest} >
            {children}
        </Typography>
    )
}

export default H1

function MyCard({classes, children,  elevation = 24, ...rest}) {
   return (
        <Card elevation = {elevation} {...rest}> 
            {children}
        </Card> 
   )  
}

and then use this in a component like:

function MyComponent({classes}) {
    return <MyCard className = {classes.root}> 
        <H1> Hello World! </H1> 
    </Card> 
}

const styles = theme => ({
     root: {
          backgroundColor: theme.palette.primary.main, 
          color: theme.palette.primary.contrastText, 
     }

}) 

export default withStyles(styles)(MyComponent); 

And basically avoid using the Material-UI compoents directly.

Is this a standard way of doing things, or is there otherwise a reason this a bad idea?

dwjohnston
  • 2,553

1 Answers1

3

This is no different than reusing snippets of template code in a server side rendering language (sometimes called "partial templates" or "partials"). Creating functions (or reusable templates) for common user interface elements is good practice when those common elements will all evolve their styles and structure together.

For instance, if a button needs a consistent style applied to it, there's nothing wrong with encapsulating this in a function.

When you have identified a good abstraction, write code to enforce that abstraction. That's just basic, good coding practice.

That's not to say you should create a Button function just to generate a button. Additional semantic meaning should drive this decision. For instance, a button that when clicked shows a confirmation dialog should always look the same way, and behave the same way. This is a great candidate for a WarningButton(confirmationMessage) function. This enforces:

  1. HTML structure
  2. Common style
  3. Common behavior

This assumes that a change in any of the three points above should be applied to all "warning" buttons in the same way.

  • Totally agreed with Greg's answer, but in case of Material UI - here is also well-known pattern as "theming". It is not so applicable for Typography (but still, you can make overrides in theme and then - just import like import { Typography as H1 } from '@material-ui/core) But you can make look all your Card components, Button components the same.
    Check MUI docs for more info: https://material-ui.com/ru/customization/theming/
    – Klimenko Kirill Oct 02 '19 at 12:13