60

I am struggling to modify button colors in Material UI (v1).

How would I set the theme to behave similarly to Bootstrap, so I could just use "btn-danger" for red, "btn-success" for green...?

I tried with custom className but it doesn't work properly (the hover color doesn't change) and it seems repetitive. What options do I have?

Olivier Tassinari
  • 8,238
  • 4
  • 23
  • 23
Nema Ga
  • 2,450
  • 4
  • 26
  • 49

14 Answers14

55

In Material UI v5, this is how you create customized colors in your theme for your Material UI Button. The primary and secondary colors are created the same way under the hood:

const { palette } = createTheme();
const { augmentColor } = palette;
const createColor = (mainColor) => augmentColor({ color: { main: mainColor } });
const theme = createTheme({
  palette: {
    anger: createColor('#F40B27'),
    apple: createColor('#5DBA40'),
    steelBlue: createColor('#5C76B7'),
    violet: createColor('#BC00A3'),
  },
});

Usage

<Button color="anger" variant="contained">
  anger
</Button>
<Button color="apple" variant="contained">
  apple
</Button>
<Button color="steelBlue" variant="contained">
  steelBlue
</Button>
<Button color="violet" variant="contained">
  violet
</Button>

If you're using typescript, you also need to add additional types for the colors you just defined:

declare module '@mui/material/styles' {
  interface CustomPalette {
    anger: PaletteColorOptions;
    apple: PaletteColorOptions;
    steelBlue: PaletteColorOptions;
    violet: PaletteColorOptions;
  }
  interface Palette extends CustomPalette {}
  interface PaletteOptions extends CustomPalette {}
}

declare module '@mui/material/Button' {
  interface ButtonPropsColorOverrides {
    anger: true;
    apple: true;
    steelBlue: true;
    violet: true;
  }
}

Live Demo

Codesandbox Demo

Related Answers

Olivier Tassinari
  • 8,238
  • 4
  • 23
  • 23
NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
  • 4
    In this way, things become complicated... – shen May 10 '22 at 18:17
  • 1
    didn't know about augmentColor, nice tip – janfabian Jul 13 '22 at 07:28
  • 5
    I disagree with @shen, this made my components much easier to style since it no longer requires setting properties for different states (hover, selected, etc.). augmentColor in particular made my theme much cleaner since it now allows me to generate the different shades of colors without having to hardcode each one. Now I can have specific variants for different button states. Thanks @NearHuscarl!! – timgcarlson Aug 30 '22 at 22:03
  • 1
    This explains `additional types for the colors` better than the `mui` documentation – Zeeshan Ahmad Khalil Nov 29 '22 at 11:21
  • Such an amazing answer! This is the right way to do it in a production codebase. – Jay Welsh Apr 01 '23 at 13:45
  • Currently trying to implement it in MUI version 5.11.15 but I got errors. The PaletteColor returned by createColor does not fit string type what is used inside objects used in createTheme(). This answer is highly rated but its a lie – Marecky Apr 10 '23 at 16:42
40

You can try this

<Button
    style={{
        borderRadius: 35,
        backgroundColor: "#21b6ae",
        padding: "18px 36px",
        fontSize: "18px"
    }}
    variant="contained"
    >
    Submit
</Button>
Banana
  • 2,435
  • 7
  • 34
  • 60
Adnan
  • 1,589
  • 11
  • 17
  • 28
    inline styles are a hacky solution – damusix Feb 27 '20 at 18:47
  • I don't think they're "hacky" (though they can conflict with CSS classes) but it's not a "comprehensive" solution & so doesn't really answer the question as written. If this is the only button that'll ever look like this, it's sufficient. If you want themed error buttons that you want to be configurable app-wide, then this is insufficient. – Lovethenakedgun Dec 15 '22 at 03:15
  • this creates conflicts and wrong hover states which you cannot fix without further hacking – ggzone Jan 27 '23 at 13:10
33

I came up with this solution using Brendans answer in this thread. Hopefully it'll help someone in a similar situation.

import React, { Component } from 'react'
import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles'
import Button from 'material-ui/Button'
import { red, blue } from 'material-ui/colors'

const redTheme = createMuiTheme({ palette: { primary: red } })
const blueTheme = createMuiTheme({ palette: { primary: blue } })

class Buttons extends Component {
  render = () => (
    <div className="ledger-actions-module">
      <MuiThemeProvider theme={redTheme}>
        <Button color="primary" variant="raised">
          Delete
        </Button>
      </MuiThemeProvider>
      <MuiThemeProvider theme={blueTheme}>
        <Button color="primary" variant="raised">
          Update
        </Button>
      </MuiThemeProvider>
    </div>
  )
}
alex marmon
  • 341
  • 3
  • 4
  • 3
    For v4 `import { ThemeProvider } from '@material-ui/styles';` `import { createMuiTheme } from "@material-ui/core/styles";` – MeanMan Sep 06 '19 at 10:34
  • 6
    This is a workaround and not a real solution. We should be able to create custom buttons using simple theme config. – damusix Feb 27 '20 at 18:51
  • 2
    A word of caution concerning performance -> what if you had a list of n items, each needing the red and the blue button? This would require n * 2 theme providers and will likely crash the browser eventually. Also not very developer friendly as this requires a lot of boilerplate code just to set a color. I posted a possible solution that will allow for ` – Adam Cooper Oct 27 '20 at 17:51
17

There is a mistake with Bagelfp's answer, and some other things to consider;

First, 'error' is not a supported color theme in material-ui@next v1's Button component. Only 'default', 'inherit', 'primary' and 'secondary' are accepted by the color prop.

Here is the approach I have found to be the easiest so far. First, choose your two most common theme colors and place them at the root of your app.

import React from 'react';
import { Component } from './Component.js'
import { MuiThemeProvider, createMuiTheme } from 'material-ui/styles';

const theme = createMuiTheme({
  palette: {
    primary: 'purple',
    secondary: 'green',
    error: 'red',
  },
});

export class App extends Component {
  render() {
    return (
      <MuiThemeProvider theme={theme}>
        <Component />
        ...
      </MuiThemeProvider>
    );
  }
}

Then in your component, choose the theme with your desired color;

import React from 'react';
import Button from 'material-ui/Button';

export const Component = (props) => (
  <div>
    <Button variant="fab" color="primary">
      I am purple, click me!
    </Button>
  </div>
)

If you need a third and fourth color, you can export Component.js with a new palette just like you did in App.js.

This is the only solution I have found that allows me to retain the darkened hover effect (none of the official override examples retain functioning hover). I really wish I could find a way to simply drop in a new theme color when calling Button, but for now this is the most simple way to do it.

EDIT: My new preferred method is to create a CustomButton component using styled-components and material-ui buttonbase. I also place the styled-components theme provider at the root of my app alongside my MuiThemeProvider. This gives me easy access to additional theme colors in all my styled-components without importing and dropping in more ThemeProviders. In the case of my CustomButton, I just give it a theme prop, which is passed right down to the css in styled(ButtonBase). See the styled-component docs for more info.

Brendan McGill
  • 6,130
  • 4
  • 21
  • 31
  • Is there any way to make it work with custom color codes like #17a2b8 ? – Arthur Attout Mar 26 '18 at 07:04
  • Yes, just enter the HEX or RGBA as part of the palette object (where I wrote purple, green, red). – Brendan McGill Mar 26 '18 at 18:06
  • 1
    The theme definition also allows for variants such as `main`, `dark` and `light` - as shown by the default theme https://material-ui.com/customization/default-theme/ Is there a way to set the color to the `dark` variant? – timbo Nov 04 '18 at 23:38
  • @timbo dark and light variants can be set as follows: const theme = createMuiTheme({ palette: { error: { main: '#e9f0f3', dark: '#c9d9e0', light: '#ffff00' } }, }); – Aniket Suryavanshi Oct 28 '20 at 13:37
  • Can you please show the last method you've explained? (on final "EDIT: ") – Cristian G Feb 18 '21 at 11:41
16

Try this:

import * as React from 'react';
import Button, { ButtonProps } from "@material-ui/core/Button";
import { Theme } from '@material-ui/core';
import { withStyles } from '@material-ui/styles';

const styles: (theme: Theme) => any = (theme) => {
    return {
        root:{
            backgroundColor: theme.palette.error.main,
            color: theme.palette.error.contrastText,
            "&:hover":{
                backgroundColor: theme.palette.error.dark
            },
            "&:disabled":{
                backgroundColor: theme.palette.error.light
            }
        }
    };
};

export const ButtonContainedError = withStyles(styles)((props: ButtonProps) => {
    const { className, ...rest } = props;
    const classes = props.classes||{};
    return <Button {...props} className={`${className} ${classes.root}`} variant="contained" />
});

Now you have a ButtonContainedError to use anywhere.

And it is consistent with your theme.

Juliano
  • 2,422
  • 21
  • 23
  • 1
    The nice thing about this is if in your theme you only specify the `main` colour, the dark and light properties seem to be calculated correctly. ex. A pink `main` value will result in the dark and light being a dark pink and a light pink respectively. – ABCD.ca Jul 02 '20 at 17:50
10

Here is an example typescript implementation:

import React from "react";
import { createStyles, Theme, makeStyles } from "@material-ui/core/styles";
import capitalize from "lodash/capitalize";

import MuiButton, {
  ButtonProps as MuiButtonProps
} from "@material-ui/core/Button";

export type ColorTypes =
  | "primary"
  | "secondary"
  | "error"
  | "success"
  | "warning"
  | "default"
  | "inherit"
  | "info";

type ButtonProps = { color: ColorTypes } & Omit<MuiButtonProps, "color">;

const useStyles = makeStyles<Theme>(theme =>
  createStyles({
    outlinedSuccess: {
      borderColor: theme.palette.success.main,
      color: theme.palette.success.main
    },
    outlinedError: {
      borderColor: theme.palette.error.main,
      color: theme.palette.error.main
    },
    outlinedWarning: {
      borderColor: theme.palette.warning.main,
      color: theme.palette.warning.main
    },
    outlinedInfo: {
      borderColor: theme.palette.info.main,
      color: theme.palette.info.main
    },
    containedSuccess: {
      backgroundColor: theme.palette.success.main,
      color: theme.palette.success.contrastText,
      "&:hover": {
        backgroundColor: theme.palette.success.dark
      }
    },
    containedError: {
      backgroundColor: theme.palette.error.main,
      color: theme.palette.error.contrastText,
      "&:hover": {
        backgroundColor: theme.palette.error.dark
      }
    },
    containedWarning: {
      backgroundColor: theme.palette.warning.main,
      color: theme.palette.warning.contrastText,
      "&:hover": {
        backgroundColor: theme.palette.warning.dark
      }
    },
    containedInfo: {
      backgroundColor: theme.palette.info.main,
      color: theme.palette.info.contrastText,
      "&:hover": {
        backgroundColor: theme.palette.info.dark
      }
    }
  })
);

const Button: React.FC<ButtonProps> = ({ children, color, ...props }) => {
  const classes = useStyles();
  const className = classes?.[`${props.variant}${capitalize(color)}`];
  const colorProp =
    ["default", "inherit", "primary", "secondary"].indexOf(color) > -1
      ? (color as "default" | "inherit" | "primary" | "secondary")
      : undefined;

  return (
    <MuiButton {...props} color={colorProp} className={className}>
      {children}
    </MuiButton>
  );
};

Button.displayName = "Button";

export default Button;

with this you can do <Button variant="contained" color="success"> with autocomplete and warning free :)

Update:

In Material UI V5 this is achievable in a much more elegant way. You can just add a color to the palette and the button will automatically support it! Their documentation has a great example of how to do this: https://mui.com/customization/palette/#adding-new-colors

Adam Cooper
  • 936
  • 11
  • 16
  • Nice solution Typescript 4.0.3 doesn't seem to like this line though: ```const className = classes?.[`${props.variant}${capitalize(color)}`];``` ```Element implicitly has an 'any' type because expression of type 'string' can't be used to index type 'Record<"root" | "textError" | "outlinedError" | "containedError", string>'.``` – Bassem Nov 17 '20 at 19:27
  • I used this approach. What i did to solve the ts error you mentioned @Bassem was this, `classes[\`${variant}${capitalize(color)}\` as keyof typeof classes]` notice the `as keyof typeof classes`. Although i'm not sure if my teammates are going to accept it, tbh. I'll have my PR for review soon. – PenguinBlues Jan 15 '21 at 04:22
  • Nice hack @PenguinBlues , please keep us updated if your teammates come up with something else, I'll uncomment the `@ts-ignore` and use your solution for now – Bassem Jan 16 '21 at 00:54
8

You can use theme.palette.getContrastText() to calculate the correct text color based on a background color value.

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

const useStyles = makeStyles((theme) => ({
  deleteButton: {
    // to make a red delete button
    color: theme.palette.getContrastText(theme.palette.error.main),
    background: theme.palette.error.main,
  }
}));

export const DeleteButton = () => {
  const classes = useStyles();
  return (
    <Button className={classes.deleteButton}>Delete</Button>
  );
}
parties
  • 81
  • 1
  • 2
6

first try to install npm install @material-ui/styles the apply styles according to material documentation, for react class component you can use below code:

import React, {Component} from "react";
import { styled } from '@material-ui/styles';
import Button from '@material-ui/core/Button';

const MyButton = styled(Button)({
    background: 'linear-gradient(45deg, #FE6B8B 30%, #FF8E53 90%)',
    border: 0,
    borderRadius: 3,
    boxShadow: '0 3px 5px 2px rgba(255, 105, 135, .3)',
    color: 'white',
    height: 48,
    padding: '0 30px',
});
class AprinClass extends Component {
    render() {
        return (
            <MyButton>Styled Components</MyButton>
        )
    }
}
export default AprinClass;

for more information with references please check my blog in medium. https://medium.com/@farbodaprin/how-to-make-a-customisable-material-ui-button-a85b6534afe5

Farbod Aprin
  • 990
  • 1
  • 10
  • 20
4

You could create a theme with palettes defined for each of their 3 supported intentions (primary, secondary, error), and then use the color prop on <Button> to use those. In your example the btn-danger could be <Button color='error'>

EDIT: Brendan's answer is correct here that error is not supported for Button. According to the documentation Button only supports intentions that "make sense for this component.", so only primary and secondary would work here.

From their docs (trimmed down a little here):

const theme = createMuiTheme({
  palette: {
    primary: purple,
    secondary: red
  }
});

function Palette() {
  return (
    <MuiThemeProvider theme={theme}>
      <div>
        <Button color="primary">{'Primary'}</Button>
        <Button color="secondary">{'Secondary'}</Button>
      </div>
    </MuiThemeProvider>
  );
}

See Brendan's Answer for a more realistic example of creating themes for your components.

Bagelfp
  • 61
  • 1
  • 7
3

The easiest way to change the button color is to add the "style" attribute. Here's an example of a green button I created:

import Button from '@material-ui/core/Button';

  <Button 
    variant="contained" 
    color="primary" 
    style={{ backgroundColor: '#357a38' }}
  >
    Run
  </Button>
  • 1
    Welcome to SO. Though we thank you for your answer, it would be better if it provided additional value on top of the other answers. In this case, your answer does not provide additional value, since Adnan shah already posted that solution. If a previous answer was helpful to you, you should [vote it up](https://stackoverflow.com/help/privileges/vote-up) once you have enough [reputation](https://stackoverflow.com/help/whats-reputation). – Maciej Goszczycki Apr 21 '21 at 00:01
1

From 2022/05:

According to the official Doc: Customize MUI with your theme, you need to use ThemeProvider and createTheme.

First, customize the primary color like this,

import {ThemeProvider, createTheme} from '@mui/material/styles';

const theme = createTheme({
  palette: {
    primary: {
      main: '#000000',
    },
  },
});

Then wrap your App or anywhere your want with the ThemeProvider component, and set the color props of your Button component to primary.

function YourApp() {
  // logic omitted

  return (
    <ThemeProvider theme={theme}>
      <YourApp>
        <Button color="primary">Click me</Button>
      </YourApp>
    </ThemeProvider>
  );
}
shen
  • 933
  • 10
  • 19
1

Export any color from '@mui/material/colors'; (Docs)

import { pink } from '@mui/material/colors';

and use like this

<Button variant="contained" sx={{ backgroundColor: pink[700] }}>Check</Button>

enter image description here

kiranvj
  • 32,342
  • 7
  • 71
  • 76
0

Use This sx props it gives more understanding and also more relative to the MUI. in most of the MUI components, they use these props.

import {Button} from '@mui/material'

<Button variant="contained" sx={{
    borderRadius: 35,
    backgroundColor: "#21b6ae",
    padding: "18px 36px",
    fontSize: "18px",
    **color:"white"**
}} >
         elevation
    </Button>
L O C O
  • 37
  • 9
-1

I found out that !important works in the classes. (React Hooks)

const styles "...etc..." = (theme: Theme) => ({
    buttonWarning: {
        backgroundColor: theme.palette.warning.dark + '!important'
    }
}))

Then in the button

const classes = styles();

<Button className={classes.buttonWarning}>Hello</Button>
PathToLife
  • 1,011
  • 10
  • 19