How to add custom MUI palette colors - reactjs

I'm trying to establish my own palette colors to match my branding in MUI. So far I can only get the primary and secondary colors to work when applied as the background color to buttons. When I add my own variable names, like use "accent" as shown as an example from MUI's website, the button defaults to grey.
Here is my MyTheme.js code:
import { createMuiTheme } from 'material-ui/styles';
import purple from 'material-ui/colors/purple';
export default createMuiTheme({
palette: {
primary: { // works
main: '#165788',
contrastText: '#fff',
},
secondary: { // works
main: '#69BE28',
contrastText: '#fff',
},
companyBlue: { // doesn’t work - defaults to a grey button
main: '#65CFE9',
contrastText: '#fff',
},
companyRed: { // doesn’t work - grey button
main: '#E44D69',
contrastText: '#000',
},
accent: { // doesnt work - grey button
main: purple, // import purple doesn’t work
contrastText: '#000',
},
},
});
Here is my App.js code:
import React, { Component } from 'react';
import Button from 'material-ui/Button';
import MuiThemeProvider from 'material-ui/styles/MuiThemeProvider';
import MyTheme from './MyTheme';
import './App.css';
import { withStyles } from 'material-ui/styles';
import PropTypes from 'prop-types';
const styles = theme => ({
button: {
margin: theme.spacing.unit,
},
input: {
display: 'none',
},
});
class App extends Component {
constructor(props)
{
super(props);
}
render() {
const { classes } = this.props;
return (
<MuiThemeProvider theme={MyTheme}>
<Button variant="raised" >
Default
</Button>
<Button variant="raised" color="primary" className={classes.button}>
Primary
</Button>
<Button variant="raised" color="secondary" className={classes.button}>
Secondary
</Button>
<Button variant="raised" color="companyRed" className={classes.button}>
Company Red
</Button>
<Button variant="raised" color="accent" className={classes.button}>
Accent
</Button>
</MuiThemeProvider>
);
}
}
App.propTypes = {
classes: PropTypes.object.isRequired,
};
export default withStyles(styles)(App);

The MUI palette is extendable, but you need to do a couple of things to create a new color and apply to your Button component.
First, let’s define a theme with a custom color variable. You can use augmentColor() to generate the PaletteColor so it can be consumed in your Button:
import { purple } from "#mui/material/colors";
const { palette } = createTheme();
const theme = createTheme({
palette: {
myAwesomeColor: palette.augmentColor({ color: purple }),
// Use this code if you want to use an arbitrary color
// myAwesomeColor: palette.augmentColor({
// color: {
// main: "#00ff00"
// }
// })
}
});
Then update your TypeScript definition, so it can recognize the property myAwesomeColor when referencing the Palette and PaletteOption objects (skip this step if you're using JavaScript). You also need to extend the Button's color props definition, because by default it only accepts the following values:
'inherit' | 'primary' | 'secondary' | 'success' | 'error' | 'info' | 'warning'
declare module "#mui/material/styles" {
interface Palette {
myAwesomeColor: string;
}
interface PaletteOptions {
myAwesomeColor: string;
}
}
declare module "#mui/material/Button" {
interface ButtonPropsColorOverrides {
myAwesomeColor: true;
}
}
The final step is to inject the custom theme and set the custom color for your Button:
<ThemeProvider theme={theme}>
<Button color="myAwesomeColor" variant="contained">
AWESOME
</Button>
</ThemeProvider>
Live Demo
Related answer
Change primary and secondary colors in MUI

Other than needing to change purple in your MyTheme to be something like purple[500], I'm not sure why this wouldn't work for you. You may not be able override anything other than the primary and secondary in this way.
Regardless, here's a workaround:
In MyTheme.js:
accent: { backgroundColor: purple[500], color: '#000' }
Then in App.js:
<Button
variant="raised"
style={MyTheme.palette.accent}
className={classes.primary}>
Accent
</Button>
A working example is here.

for MUI v5 this seems to work for me:
let theme = createTheme({
palette: {
customPrimary: {
main: '#ff3402',
},
},
});
theme = createTheme(theme, {
components: {
MuiAppBar: {
styleOverrides: {
colorPrimary: {
backgroundColor: theme.palette.customPrimary.main,
},
},
},
}});

For those creating a custom color in their theme in Typescript this is how I did it.
I added the yellow and dark color types:
interface Palette {
dark?: Palette['primary'];
yellow?: Palette['primary'];
}
interface PaletteOptions {
dark?: PaletteOptions['primary'];
yellow?: PaletteOptions['primary'];
}
}
Then set them based on type. This took me a second to figure out. While I added dark a type on PaletteOptions, it's type then became like the rest as PaletteColorOptions which has the following type:
export type PaletteColorOptions = SimplePaletteColorOptions | ColorPartial;
export interface SimplePaletteColorOptions {
light?: string;
main: string;
dark?: string;
contrastText?: string;
}
So I used main to define the color in it's type.
const theme = createTheme({
dark: {
main: '#5b504b'
},
yellow: {
main: '#f4e6c2',
dark: '#e1d7bd',
}
}
Hope that helps anyone with the Typescript typing problem.

There shouldn't be a problem adding a custom palette as follows:
import { ThemeProvider, createTheme } from "#mui/material/styles";
const theme = createTheme({
palette: {
primary: {
main: "#2a9461"
},
secondary: {
main: "#494c7d"
},
companyRed: {
main: "#E44D69",
contrastText: "#000",
},
}
});
Using MUI colors like "purple" has to be accessed by the object key because each color comes with shade values from 50 to 900 (light to dark):
import { purple } from "#mui/material/colors";
import { ThemeProvider, createTheme } from "#mui/material/styles";
const theme = createTheme({
palette: {
primary: {
main: "#2a9461"
},
secondary: {
main: "#494c7d"
},
companyRed: {
main: '#E44D69',
contrastText: "#000",
},
accent: {
main: purple[50] // Or purple[100], purple[200]
}
}
});
But if you are looking to create your own purple palette (and other colors) to match the branding, you can add a new js file with all custom colors and shades and import it in the main app:
colors.js file
export const green = {
50: "#dbece2",
100: "#a7d0b8",
200: "#6eb18b",
300: "#2a9461",
400: "#008044",
500: "#006c27",
600: "#006020",
700: "#005116",
800: "#00410b",
900: "#002700"
};
export const purple = {
50: "#e9eaf0",
100: "#c7cada",
200: "#a3a8c1",
300: "#8186a7",
400: "#686c95",
500: "#505485",
600: "#494c7d",
700: "#414371",
800: "#393964",
900: "#2d2a4c"
};
import { green, purple } from "./colors";
import { ThemeProvider, createTheme } from "#mui/material/styles";
const theme = createTheme({
palette: {
primary: {
main: green[300]
},
secondary: {
main: purple[600]
},
primaryLight: {
main: green[50]
}
}
});
Here is a more detailed explanation of creating custom themes, palettes, and colors https://muhimasri.com/blogs/how-to-customize-theme-and-colors-in-material-ui/

Related

MUI can't read palette value

I am trying to use typescript with MUI in reactJS ,but it keep giving me this error , I have tried to uninstall MUI packages and then install it again but nothing changes, it don't give me any error in typescript so it hard to tell why this error happen
TypeError: Cannot read properties of undefined (reading 'primary')
I have this setup for MUI
My theme configuration
export type Mode = "dark" | "light";
declare module '#mui/material/styles' {
interface Palette {
general:{
purple: string,
grayLight: string,
linear: string,
white: string
}
}
interface PaletteOptions {
general?:{
purple: string,
grayLight: string,
linear: string,
white: string
}
}
}
export const themeConfig = (mode: Mode) => {
return {
palette: {
mode: mode,
primary: {
main: primMain,
dark: primDark,
light: primLight,
},
secondary: {
main: secMain,
dark: secDark,
light: secLight,
},
error: {
main: errorMain,
dark: errorDark,
},
success: {
main: successMain,
dark: sucessDark,
},
text: {
primary: textPrimary,
secondary: textSecondary,
},
general: {
purple: commonPurple,
grayLight: commonGrayLight,
linear: comminLinear,
white: commonWhite
},
},
};
};
Create theme init
import { createTheme} from '#mui/material/styles';
import {themeConfig} from '../styles/styleTheme'
const theme = createTheme(themeConfig('dark'))
Use makeStyles method
import {makeStyles,createStyles } from '#mui/styles';
import { Theme } from '#mui/material/styles';
const useStyles = makeStyles((theme:Theme) => ({
root: {
backgroundColor: theme.palette.primary.dark,
},
}),
);
Used in component
so when I use this ('text.secondary') it work fine but when I use this (classes.root) it throw me the error I mentioned above
import { Button } from '#mui/material';
import { ThemeProvider} from '#mui/material/styles';
const classes = useStyles();
<ThemeProvider theme={theme}>
<Button className={classes.root} sx={{color:'text.secondary'}}>style</Button>
</ThemeProvider>
My react component
import React from 'react'
import {useTranslation} from 'react-i18next'
import { ThemeProvider} from '#mui/material/styles';
import {makeStyles,createStyles } from '#mui/styles';
import theme from './config/createTheme';
import {handleLangChange} from './services/changeLang'
import { Button } from '#mui/material';
import { Theme } from '#mui/material/styles';
const useStyles = makeStyles((theme:Theme) => ({
root: {
backgroundColor: theme.palette.primary.dark,
},
}),
);
function App() {
const {t} = useTranslation()
const classes = useStyles();
return (
<ThemeProvider theme={theme}>
<Button className={classes.root} sx={{color:'text.secondary'}}>style</Button>
</ThemeProvider>
);
}
export default App;
You are calling useStyles to create a stylesheet and reference the theme object in the root component where you also return the ThemeProvider to pass the theme down, so the theme is not available there. To fix it, move your useStyles call to a child component where it can access the theme from the parent:
<ThemeProvider theme={theme}>
<Content />
</ThemeProvider>
const Content = () => {
const classes = useStyles();
return (
<Button className={classes.root} sx={{ color: 'text.secondary' }}>
style
</Button>
);
};
Also note that it's not recommended to use useStyles/withStyles in v5 because they're deprecated APIs.
EDIT: You can see system section for more detail. This is how it'd look in v5:
import MuiButton from '#mui/material/Button';
const Button = styled(MuiButton)(({ theme }) => ({
color: theme.palette.secondary.main,
}));
sx prop:
<Button sx={{ color: 'secondary.main' }}

Material-UI Color Prop Options

I currently have the following theme using material-ui:
import { createMuiTheme } from "#material-ui/core/styles";
import { blue, green } from "#material-ui/core/colors";
const theme = createMuiTheme({
palette: {
primary: blue,
secondary: green,
},
});
export default theme;
Now, if I do a console.log of the green object that I import from material-ui colors, I get a color object like this:
green = {
50: "#e8f5e9"
100: "#c8e6c9"
200: "#a5d6a7"
300: "#81c784"
400: "#66bb6a"
500: "#4caf50"
600: "#43a047"
700: "#388e3c"
800: "#2e7d32"
900: "#1b5e20"
A100: "#b9f6ca"
A200: "#69f0ae"
A400: "#00e676"
A700: "#00c853"
}
As such, I thought I could style my AppBar component as follows: <AppBar color="secondary.800">.
This, however, does not work. I also tried: <AbbBar color="secondary[800]"> and that also does not work.
I have even tried this variation with the theme:
import { createMuiTheme } from "#material-ui/core/styles";
import { blue, green } from "#material-ui/core/colors";
const theme = createMuiTheme({
palette: {
primary: blue,
secondary: {...green},
},
});
export default theme;
This also does not work.
Is there a way for me to access the different color variations in the green color object vis-a-vis the color prop in the AppBar component?
If so, how?
Thanks.
What you are trying to do is not possible with Material UI. You can only set the color variant (primary or secondary) and the color will be taken from your theme.
To manually set a different color, you will need to override the component's style.
import { withStyles } from "#material-ui/core/styles";
import { blue } from "#material-ui/core/colors";
const MyChildComponent = ({ classes }) => <AppBar classes= {{ root: classes.root }} />
const styles = () => ({
root: {
backgroundColor: blue[400],
}
});
export default withStyles(styles)(MyChildComponent);
--------
import { withStyles } from "#material-ui/core/styles";
import { green } from "#material-ui/core/colors";
const MyParentComponent = ({ classes }) => <MyChildComponent classes={{ root: classes.someClass}} />
const styles = () => ({
someClass: {
backgroundColor: green[400],
}
});
export default withStyles(styles)(MyParentComponent);
Of course you could define some internal logic to decide which class to apply in your child component based on some prop that you pass down.
import clsx from "clsx";
import { withStyles } from "#material-ui/core/styles";
import { red, green, blue } from "#material-ui/core/colors";
const MyChildComponent = ({ color, classes }) => <AppBar className={clsx({
[classes.red]: color === "red",
[classes.blue]: color === "blue,
[classes.green]: color === "green",
})}/>
const styles = () => ({
red: {
backgroundColor: red[400],
},
green: {
backgroundColor: green[400],
},
blue: {
backgroundColor: blue[400],
},
});
export default withStyles(styles)(MyChildComponent);
--------
const MyParentComponent = () => <MyChildComponent color="red"/>
export default MyParentComponent;
Personally, I would just go for manually overriding the classes as shown above.
As per Material UI documentation, you can access color variations with array notation and define theme like
const theme = createMuiTheme({
palette: {
primary: {
main: blue[600]
},
secondary: {
main: green[800]
}
}
});
Working Code sandbox link: https://codesandbox.io/s/material-colors-9hnqh?file=/src/App.js

Material UI doesn't use overridden styles when using useTheme hook

I'm trying to use custom theming in Material UI like so:
const theme = createMuiTheme({
palette: {
primary: {
main: PRIMARY_COLOR, // "#121212"
},
secondary: {
main: SECONDARY_COLOR, // "#F7D600"
},
},
});
const Wrapper = ({ children }) => {
return (
<ThemeProvider theme={theme}>{children}</ThemeProvider>
);
};
This works for things like buttons:
<Button
variant="contained"
color="secondary"
/>
In this case, the hex color #F7D600 gets applied.
But when I try to use the same color on my components using makeStyles, it doesn't seem to recognize it. It just uses the default by Material UI:
const useStyles = makeStyles((theme) => ({
someElement: {
backgroundColor: theme.palette.primary.main // <- not working. it uses the default purple color
}
});
I also tried useTheme but it's the same result:
const SomeComponent = () => {
const theme = useTheme();
return (
<Box style={{ backgroundColor: theme.palette.primary.main }}></Box>
);
}
Any ideas what I could be missing?
I can't see your import statement, but I use MuiThemeProvider. That could be your issue. Everything else looks right to me
import { MuiThemeProvider } from '#material-ui/core';
Use the below style to override any component in material-UI
import { createMuiTheme, colors } from '#material-ui/core'
const theme = createMuiTheme({
palette: {
background: {
dark: '#F4F6F8',
default: colors.common.white,
paper: colors.common.white,
},
primary: {
main: colors.indigo[500],
},
secondary: {
main: colors.indigo[500],
},
text: {
primary: colors.blueGrey[900],
secondary: colors.blueGrey[600],
},
common: {
tableHeader: '#DEF3FA',
},
action: {
oddRowColor: '#def3fa2e',
},
},
zIndex: {
modal: 10010, // override modal zIndex
appBar: 1000, // override Appbar
},
overrides: { 'MTableHeader-header': { root: { width: '143px !important' } } },
})
// use in your component
const StyledTableRow = withStyles((theme) => ({
root: {
'&:nth-of-type(odd)': {
backgroundColor: theme.palette.action.oddRowColor,//defined in theme
},
padding: 'dense',
},
}))(TableRow)
[https://material-ui.com/customization/default-theme/][1]

How to get rid of Typescript type error when overriding MuiContainer classes?

My app consists of two files: (https://codesandbox.io/s/react-ts-muicontainer-override-yywh2)
//index.tsx
import * as React from "react";
import { render } from "react-dom";
import { MuiThemeProvider } from "#material-ui/core/styles";
import { Button, Container, Typography } from "#material-ui/core";
import myTheme from "./myTheme";
function App() {
return (
<MuiThemeProvider theme={myTheme}>
<Container>
<Typography>
<p>Some text</p>
</Typography>
<Button>dummy</Button>
</Container>
</MuiThemeProvider>
);
}
const rootElement = document.getElementById("root");
render(<App />, rootElement);
//myTheme.ts
import { createMuiTheme } from "#material-ui/core/styles";
export default createMuiTheme({
overrides: {
MuiButton: {
root: {
backgroundColor: "red"
}
},
MuiTypography: {
root: {
color: "green"
}
},
MuiContainer: {
root: {
backgroundColor: "yellow"
}
}
}
});
The app displays the content on yellow background which means that my theme overrides work. However, the whole MuiContainer key in myTheme.ts is underlined red and the error says:
Argument of type '{ MuiButton: { root: { backgroundColor: string; };
}; MuiTypography: { root: { color: string; }; }; MuiContainer: { root:
{ backgroundColor: string; }; }; }' is not assignable to type
'Overrides'.
Object literal may only specify known properties, and 'MuiContainer' does not exist in type 'Overrides'.ts(2345)
createMuiTheme.d.ts(20, 3): The expected type comes from property
'overrides' which is declared here on type 'ThemeOptions'
Indeed, it seems that MuiContainer is missing from interface ComponentNameToClassKey in mui overrides.d.ts. However, the MuiContainer documentation says: If using the overrides key of the theme, you need to use the following style sheet name: MuiContainer. So I think the key should be correct.
How to fix the typescript type checking in this case?
Passing an extended interface from ThemeOptions as
interface ThemeType extends ThemeOptions {
overrides: Object
}
works fine for me
For fixing the typescript issue what you can do is give createMuiTheme as type any for a quick workaround.
import { createMuiTheme } from "#material-ui/core/styles";
export default (createMuiTheme as any)({ // Fixes the typescript warning.
overrides: {
MuiButton: {
root: {
backgroundColor: "red"
}
},
MuiTypography: {
root: {
color: "green"
}
},
MuiContainer: {
root: {
backgroundColor: "yellow"
}
}
}
})
I hope it helps. Thanks!

Change primary color dynamically in MUI theme

There is requirement, I want to give access to user can select their primary color from list like Blue, Orange Green. I have used latest MUI for front-end.
Now I am able to change light to dark theme but my requirement is change primary color also. please help me for same how to code for same.
Please check attached screen:
import React from 'react';
import { render } from 'react-dom';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import Root from './Root';
import lightTheme from 'your-light-theme-path';
import darkTheme from 'your-dark-them-path';
const theme1 = createMuiTheme(lightTheme);
const theme2 = createMuiTheme(darkTheme)
class App extends React.Component {
state = {
isThemeLight: true;
}
onChange = () => {
this.setState=({ isThemeLight: false })
}
render() {
const { isThemeLight } = this.state;
return (
<MuiThemeProvider theme={isThemeLight ? theme1 : theme2}>
<Root /> // your app here
<button onClick={this.onChange}>Change Dark</button>
</MuiThemeProvider>
);
}
}
render(<App />, document.querySelector('#app'));
Where your lightTheme or darkTheme can be a file like this
export default {
direction: 'ltr',
palette: {
type: 'light',
primary: {
main: '#37b44e',
},
secondary: {
main: '#000',
},
},
};
You can see all the list of theme configurable in Material UI Docs Theme Configuration
Approach 2 (For Theme Change Runtime)
import React from 'react';
import { render } from 'react-dom';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import Root from './Root';
const theme1 = createMuiTheme(lightTheme);
const theme2 = createMuiTheme(darkTheme)
class App extends React.Component {
state = {
theme1: {
palette: {
type: 'light',
primary: { main: '#37b44e' },
secondary: { main: '#000' },
},
};
theme2: {
palette: {
type: 'light',
primary: { main: '#37b44e' },
secondary: { main: '#000' },
},
};
isThemeLight: true;
}
onChange = () => {
this.setState=({ isThemeLight: false })
}
onChangeTheme1 = () => {
this.setState(({theme1}) => ({
theme1: {
...theme1,
primary: { main: 'red' },
}
}));
}
render() {
const { theme1, theme2, isThemeLight } = this.state;
return (
<MuiThemeProvider
theme={isThemeLight ? createMuiTheme(theme1) : createMuiTheme(theme2)}
>
<Root /> // your app here
<button onClick={this.onChange}>Change Dark</button>
<button onClick={this.onChangeTheme1}>Change Palette Theme 1</button>
</MuiThemeProvider>
);
}
}
render(<App />, document.querySelector('#app'));
You can use useMemo to compute the theme object whenever the primaryColor state changes. The state can be updated with setPrimaryColor() after the user chose an option in the UI:
const [primaryColor, setPrimaryColor] = React.useState(defaultColor);
const theme = React.useMemo(
() =>
createTheme({
palette: {
primary: {
main: primaryColor,
},
},
}),
[primaryColor],
);
Here is a minimal working example:
const defaultColor = '#f44336';
const [primaryColor, setPrimaryColor] = React.useState(defaultColor);
const theme = React.useMemo(
() =>
createTheme({
palette: {
primary: { main: primaryColor },
},
}),
[primaryColor],
);
return (
<ThemeProvider theme={theme}>
<Box m={2}>
<TextField
select
sx={{ width: 100 }}
label="Primary color"
defaultValue={defaultColor}
onChange={(e) => setPrimaryColor(e.target.value)}
>
<MenuItem value="#f44336">Red</MenuItem>
<MenuItem value="#2196f3">Blue</MenuItem>
<MenuItem value="#4caf50">Green</MenuItem>
</TextField>
<Checkbox defaultChecked />
</Box>
</ThemeProvider>
);
Live Demo

Resources