Material-UI custom theming - reactjs

I want to create a custom theme and customize some Material-UI components. I followed this customization tutorial provided by Material-UI. With this, I was able to do the following:
Creating costume theme:
//MUI THEMING
import {
createMuiTheme,
makeStyles,
ThemeProvider,
} from "#material-ui/core/styles";
import Theme from "./../../theme";
const useStyles = makeStyles((theme) => ({
root: {
backgroundColor: Theme.palette.primary.main,
},
}));
const theme = createMuiTheme({
normal: {
primary: Theme.palette.primary.main,
secondary: Theme.palette.secondary.main,
},
});
Using costume theme:
<ThemeProvider theme={theme}>
<AppBar
position="static"
classes={{
root: classes.root,
}}>
...
</AppBar>
</ThemeProvider>
As expected, this resulted in a costume colored AppBar:
However, when I tried the same with bottom navigation, -trying to change the default primary color-, I could not get it to work. I figured that based on the tutorial, I have to use "&$selected": in the create them to get it to work, but even with this my best result was something like this:
How do I change the primary color of Bottom Navigation with no label?
Sidenote: While I was searching for the solution, I stumbled on the default theme ovject. How can I access this, how can I overwrite it?

In my project, I create a global MUI theme to override the default theme. In makeStyle you can pass a param theme in the callback function like this to get the whole MUI theme object:
const useStyles = makeStyles(theme => {
console.log(theme) // print mui global theme object
return {...your classes here}
})
After that, copy this object to a new file like muiTheme.js to create your own theme. Change these values in this object that you want to override.
// muiTheme.js
import { createMuiTheme } from '#material-ui/core/styles'
const theme = createMuiTheme({
breakpoints: {
keys: ['xs', 'sm', 'md', 'lg', 'xl'],
values: {
xs: 0,
sm: 600,
md: 960,
lg: 1280,
xl: 1920,
},
},
...
})
export default theme
Then, in index.js, use ThemeProvider to override MUI's theme.
import { ThemeProvider } from '#material-ui/core/styles'
import muiTheme from './theme/muiTheme'
import App from './App'
const Root = () => (
<BrowserRouter>
<ThemeProvider theme={muiTheme}>
<App />
</ThemeProvider>
</BrowserRouter>
)
ReactDOM.render(<Root />, document.getElementById('root'))

Related

Passing custom themes into withStyles

I have created a theme that I'm trying to call in my useStyles. This is the theme
import { createMuiTheme } from "#material-ui/core/styles";
const customTheme = createMuiTheme({
palette: {
primary: {
main: "#00ffff"
},
secondary: {
light: "#214b63",
main: "#000000",
dark: "#00660f",
contrastText: "#fff"
}
}
});
export default customTheme;
I want to use the secondary color as part of the override:
import React from "react";
import { Grid, Typography } from "#material-ui/core";
import { withStyles, MuiThemeProvider } from "#material-ui/core/styles";
import customTheme from "./newTheme";
const useStyles = (theme) => ({
root: {
color: theme.palette.secondary.main
},
grid: {
height: "100vh"
}
});
class CustomThemes extends React.Component {
render() {
const { classes } = this.props;
return (
<MuiThemeProvider theme={customTheme}>
<Grid
className={classes.grid}
container
justify="center"
alignItems="center"
>
<Grid item>
<Typography className={classes.root}>
Upgraded to withStyles theming problem
</Typography>
</Grid>
</Grid>
</MuiThemeProvider>
);
}
}
export default withStyles(useStyles)(CustomThemes);
I'm trying to pass the theme colors into the useStyles, so that the root color can work. Is there any way for me to pass the theme color into my override? I read that there is a way with makeStyles, but all my components are class-based, and converting all of them into hooks would not be possible. I need this because I have a similar component being used at multiple places. It would be convenient to be able to control the color palettes from one place.
I have tried doing it in this manner as well, but this doesn't take in the custom theme.:
const useStyles = (customTheme) => ({
root: {
color: customTheme.palette.secondary.main
},
grid: {
height: "100vh"
}
});
Another way I've tried is
export default withStyles(useStyles, { withTheme: true })(CustomThemes);
I have a css injection as well, but wanted to know it can be done this way (what I believe is the native-to-mui way). Appreciate all the help!
Found the answer. Didn't know this, but I can call the theme directly into my useStyles:
const useStyles = (theme) => ({
root: {
color: customTheme.palette.secondary.main
},
grid: {
height: "100vh"
}
});
If anyone knows why I don't need to inject the theme directly in, would greatly help in understanding why this works. I spent almost 2 days trying to pass the theme in.

Add custom theme variable in createTheme()

By default the MUI theme is a combination of several pre-defined objects such as typography: {...}, palette: {...} etc.
Is is possible to add a custom object into this setup and still use createTheme?
So for example the theme object would become:
const theme = {
palette: {
primary: '#000'
},
typography: {
body1: {
fontFamily: 'Comic Sans'
}
},
custom: {
myOwnComponent: {
margin: '10px 10px'
}
}
}
Yes, this works just fine. Material-UI does a deep merge of its defaults with the object you provide with some special handling for keys that get merged in a more sophisticated fashion (such as palette, typography and a few others). Any unrecognized keys will come through unchanged.
Below is a working example:
import React from "react";
import ReactDOM from "react-dom";
import {
useTheme,
createMuiTheme,
MuiThemeProvider
} from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
import Typography from "#material-ui/core/Typography";
const theme = createMuiTheme({
palette: {
primary: {
main: "#00F"
}
},
typography: {
body1: {
fontFamily: "Comic Sans"
}
},
custom: {
myOwnComponent: {
margin: "10px 10px",
backgroundColor: "lightgreen"
}
}
});
const MyOwnComponent = () => {
const theme = useTheme();
return (
<div style={theme.custom.myOwnComponent}>
Here is my own component using a custom portion of the theme.
</div>
);
};
function App() {
return (
<MuiThemeProvider theme={theme}>
<div className="App">
<Button variant="contained" color="primary">
<Typography variant="body1">
Button using main theme color and font-family
</Typography>
</Button>
<MyOwnComponent />
</div>
</MuiThemeProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
You can add custom variables in your MUI theme as easy as:
const theme = createTheme({
myField: {
myNestedField: 'myValue',
},
});
But if you're using Typescript, you also need to update the definition of ThemeOptions and Theme using module augmentation:
declare module '#mui/material/styles' {
// fix the type error when referencing the Theme object in your styled component
interface Theme {
myField?: {
myNestedField?: string;
};
}
// fix the type error when calling `createTheme()` with a custom theme option
interface ThemeOptions {
myField?: {
myNestedField?: string;
};
}
}
If you want to reuse the type between Theme and ThemeOptions, you can define a common interface and inherit it in both places:
declare module '#mui/material/styles' {
interface CustomTheme {
myField?: {
myNestedField?: string;
};
}
interface Theme extends CustomTheme {}
interface ThemeOptions extends CustomTheme {}
}
Also note that you don't have to create custom variables in MUI theme if you want to override a custom component using createTheme(). See this answer for more detail.
Live Demo
On top of the answers above, if you are using sx for styling you can access the custom theme like so:
<div sx={{ margin: (theme) => theme.custom.myOwnComponent.margin }} />

Override Material UI Button Text

Material UI button defaults the text within the button to uppercase. I want to override the text with the button to be the same as I have typed and not be uppercase.
I have tried to override the styling by using texttransform - none
viewButton:
{
backgroundColor: "#00D2BC",
radius: "3px",
color: "#FFFFFF",
texttransform: "none"
}
<Button
className={classes.viewButton}
data-document={n.id}
onClick={this.handleView}
>
View Document
</Button>
Can anyone help with this.
Thanks
The only problem I see with the code in your question is that you have "texttransform" instead of "textTransform".
This aspect of the buttons is controlled by the theme (here, here, and here) so it is also possible to change this via the theme. I have demonstrated both approaches in the code below.
import React from "react";
import ReactDOM from "react-dom";
import {
makeStyles,
createMuiTheme,
MuiThemeProvider
} from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
const useStyles = makeStyles({
button: {
textTransform: "none"
}
});
const defaultTheme = createMuiTheme();
const theme = createMuiTheme({
typography: {
button: {
textTransform: "none"
}
}
});
function App() {
const classes = useStyles();
return (
<MuiThemeProvider theme={defaultTheme}>
<Button>Default Behavior</Button>
<Button className={classes.button}>Retain Case Via makeStyles</Button>
<MuiThemeProvider theme={theme}>
<Button>Retain Case Via theme change</Button>
</MuiThemeProvider>
</MuiThemeProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here's a similar example but for v5 of Material-UI:
import React from "react";
import ReactDOM from "react-dom";
import { styled, createTheme, ThemeProvider } from "#material-ui/core/styles";
import Button from "#material-ui/core/Button";
const StyledButton = styled(Button)(`
text-transform: none;
`);
const defaultTheme = createTheme();
const theme1 = createTheme({
typography: {
button: {
textTransform: "none"
}
}
});
const theme2 = createTheme({
components: {
MuiButton: {
styleOverrides: {
root: {
textTransform: "none"
}
}
}
}
});
function App() {
return (
<ThemeProvider theme={defaultTheme}>
<Button>Default Behavior</Button>
<StyledButton>Retain Case Via styled</StyledButton>
<ThemeProvider theme={theme1}>
<Button>Retain Case Via theme change</Button>
</ThemeProvider>
<ThemeProvider theme={theme2}>
<Button>Retain Case Via alternate theme change</Button>
</ThemeProvider>
</ThemeProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
For those who don't wanna go an do this everywhere inside each components try global ovverrides,
const myTheme = createMuiTheme({
overrides: {
MuiButton: {
root: {
textTransform: 'none'
}
}
},
});
You make a theme object like so, and provide it to the theme provider which should wrap your app component in the index.js file
<ThemeProvider theme={myTheme}>
<PersistGate loading={null} persistor={persistor}>
<App />
</PersistGate>
</ThemeProvider>
Imports:
import { createMuiTheme, ThemeProvider } from '#material-ui/core/styles';
Per the docs, you should use the label class to override the text-transform property:
Use this style:
viewButtonLabel: { textTransform: "none" }
With this button:
<Button
className={classes.viewButton}
data-document={n.id}
onClick={this.handleView}
classes={{ label: classes.viewButtonLabel }}
>
This seems to work with V5
<Button sx={{ textTransform: 'none' }}>
Label
</Button>
https://mui.com/system/the-sx-prop/
I used Typography without playing with stying!
<Button>
<Typography style={{ textTransform: 'none' }}>Test Button</Typography>
</Button>
The answer of #Shamseer Ahammed provided the clue that finally solved this problem for me.
The use of the "overrides" property in the custom theme finally did the trick for me. I've spent days trying to do this with MuiTab instead of Button. I THINK this is the heuristic:
Use "overrides" in my theme to specify that I want to customize components
Use the component name (MuiButton or MuiTab) to specify a component to customize
Provide a key:value pair for the customization (textTransform: 'none').
I really wish the material-ui docs would make usage patterns like this more clear. I'd like there to be a middle-ground between trivial HelloWorld examples and the Component API nitty-gritty.
At least for me and my purposes, the answer from Shamseer Ahammed opened the door to my solution in a way that accepted answer did not.
import { createTheme, ThemeOptions } from '#mui/material/styles'
const defaultTheme: ThemeOptions = {
typography: {
button: {
textTransform: 'none',
},
},
}
export const lightTheme = createTheme({
palette: {
mode: 'light',
primary: {
main: '#ff2449',
light: '#ff6675',
dark: '#c40022',
},
},
...defaultTheme,
})
export const darkTheme = createTheme({
palette: {
mode: 'dark',
primary: {
main: '#f93c5b',
light: '#ff7588',
dark: '#bf0032',
},
},
...defaultTheme,
})
The MUI text transformation really sucks
I ended up changing the transformation for all the components which works great for me.
import { createTheme } from "#mui/material/styles";
import { ThemeProvider } from "#mui/material";
const theme = createTheme({
typography: {
allVariants: {
textTransform: "none",
},
},
});
<ThemeProvider theme={theme}>....</ThemeProvider>
instead of this long answer i will suggest a short answer
<Button>
<span style={{textTransform: 'none'}}>View Document</span>
</Button>

Material-UI React: Global theme override for Paper component

I have a React app built with the latest Material-UI component library.
I use many instances of the Paper component. I want to apply margins and padding to all them at once, without manually repeating this process for every instance.
I looked up the Material-UI documentation on the topic, and from what I can tell, the following code should correctly override how Paper looks:
const theme = createMuiTheme({
overrides: {
Paper: {
root: {
padding: '10px',
marginBottom: '10px',
},
},
},
});
Below is where overridden style should apply:
<ThemeProvider theme={theme}>
{/* ... */}
<Paper>
Content goes here
</Paper>
{/* ... */}
</ThemeProvider>
But the overridden values aren't being applied. Any suggestions as to what's going on?
Thanks!
in your App.js add (please note MuiPaper and not Paper):
const theme = createMuiTheme({
overrides: {
MuiPaper: {
root: {
padding: '10px',
marginBottom: '10px'
},
},
}
});
at the same App.js file, wrap your HTML:
class App extends Component {
render() {
return (
<MuiThemeProvider theme={theme}>
<div className="App">
<YourComponent1 />
<YourComponent2 />
...
</div>
</MuiThemeProvider>
);
}
}
this way, any Paper component rendered by React will have your CSS.
BTW, I created MUI schematics module which adds Material UI support, generates several Material UI components automatically and adds general theme rules in App.js. You are welcome to take a look / try it...
You can also use your CssBaseline component if you're already using it for global reset...all depends on how you're structuring your theme.
Eg below is from the Mui docs:
<CssBaseline.js>
const theme = createMuiTheme({
overrides: {
MuiCssBaseline: {
'#global': {
html: {
WebkitFontSmoothing: 'auto',
},
},
},
},
});
// ...
return (
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>
);
You can get more details from the docs here using CssBaseline to inject global theme overrides.
I found out the problem.
The internal component name used for CSS is MuiPaper, not simply Paper. The following produces the desired result:
overrides: {
MuiPaper: {
root: {
padding: '10px',
marginBottom: '10px',
},
},
},
createMuiTheme is depreciated in MUI5.
Instead, you should be using createTheme as follows:
export const myTheme = createTheme({
components:{
MuiPaper:{
defaultProps:{
sx:{
padding: '10px',
marginBottom: '10px'
}
}
}
}
})
Additionally, make sure that you wrap your app in the Theme component and add the CssBaseline component:
import { ThemeProvider } from '#mui/material';
import { myTheme } from './themes/my-theme';
import CssBaseline from '#mui/material/CssBaseline';
<ThemeProvider theme={myTheme}>
<CssBaseline />
<MyApp/>
</ThemeProvider>
Here is the official documentation for MUI5

Responsive Typography in MUI?

Designs commonly have smaller headline fonts for mobile designs.
Does MUI have a mechanism for making the typography responsive?
I see that the default theme has the font sizes defined in rems - does that mean it's a matter of just reducing the base font-size? (That doesn't seem to work, what if you want to reduce the headline fonts at different rates).
Update
MUI v4 has responsive typography built in! Check here for details.
Old response
#Luke's answer is great. I wanted to add some detail to make this work in practice, because both breakpoints and pxToRem are accessible on the theme object... making this becomes a chicken and egg problem! My approach:
import { createMuiTheme } from "#material-ui/core"
const defaultTheme = createMuiTheme({ ... customisations that don’t rely on theme properties... })
const { breakpoints, typography: { pxToRem } } = defaultTheme
const theme = {
...defaultTheme,
overrides: {
MuiTypography: {
h1: {
fontSize: "5rem",
[breakpoints.down("xs")]: {
fontSize: "3rem"
}
}
}
}
}
export default theme
None of the other answers uses the variant preset.
The best way to MUI v5, and use variant:
<Typography sx={{ typography: { sm: 'body1', xs: 'body2' } }} >
Hello world!
</Typography>
Update
The latest version of Material UI (v4) fully supports response typography. See the official documentation for details.
Original Answer
As of version 1.x, Material UI does not have a specific mechanism for handling responsive typography.
You can scale the size of all MUI Typography by changing the font-size of the <html> element, as you mentioned. (docs)
const styles = theme => ({
"#global": {
html: {
[theme.breakpoints.up("sm")]: {
fontSize: 18
}
}
}
}
Theme overrides
As far as i know, the only other option is to use theme overrides to define custom styles for each of the Typography variants.
This requires replicating some of the logic in createTypography.js (ie setting line heights to maintain vertical rhythm)
const theme = createMuiTheme({
overrides: {
MuiTypography: {
headline: {
fontSize: pxToRem(24),
[breakpoints.up("md")]: {
fontSize: pxToRem(32)
}
}
}
}
As described in the docs you can use the responsiveFontSizes() helper to make Typography font sizes in the theme responsive.
import { createMuiTheme, responsiveFontSizes } from '#material-ui/core/styles';
let theme = createMuiTheme();
theme = responsiveFontSizes(theme);
MUI v5 adds sx prop to all MUI components which supports responsive values where you can pass an object to define the values at different breakpoints:
<Typography
sx={{
fontSize: {
lg: 30,
md: 20,
sm: 15,
xs: 10
}
}}
>
This text is resized on different screen breakpoints
</Typography>
Similar to Box, Typography also supports system properties so you can skip the sx prop and pass the fontSize property directly. The code below is the same as above, just a bit shorter to write:
<Typography
fontSize={{
lg: 30,
md: 20,
sm: 15,
xs: 10
}}
>
This text is resized on different screen breakpoints
</Typography>
My approach with v5
import { createTheme } from "#mui/material";
let theme = createTheme({
// ...
});
theme = createTheme(theme, {
typography: {
h1: {
fontSize: 53,
[theme.breakpoints.only("sm")]: {
fontSize: 71,
},
},
},
});
export default theme;
This seem to be working for me v5
in App.js
...
import {
createTheme,
responsiveFontSizes,
ThemeProvider,
} from '#mui/material/styles';
let theme = createTheme();
theme = responsiveFontSizes(theme);
...
function App() {
return (
<ThemeProvider theme={theme}>
<Router>
.....
</Router>
</ThemeProvider>
);
}
"To automate this setup, you can use the responsiveFontSizes() helper to make Typography font sizes in the theme responsive."
https://mui.com/customization/typography/#responsive-font-sizes

Resources