Global Styles with React and MUI - reactjs

I'm new to React and MUI but I have to write an enterprise application with a nice styling. I would like to use some kind of global styles for my application (to be able to change it later on
) with functional components in react (maybe I will later add redux).
What's the best practice approach for global styles with react and material (latest versions)?
What about this one (ThemeProvider): https://material-ui.com/styles/advanced/?
I read about MuiThemeProvider but could not find it in the material version 4 documentation. Is it obsolete? What's the difference between MuiThemeProvider and ThemeProvider?
React (client side rendering) & Material (latest versions)
Backend: Node

In Material-UI v5, you can use GlobalStyles to do exactly that. From what I know, GlobalStyles is just a wrapper of emotion's Global component. The usage is pretty straightforward:
import GlobalStyles from "#mui/material/GlobalStyles";
<GlobalStyles
styles={{
h1: { color: "red" },
h2: { color: "green" },
body: { backgroundColor: "lightpink" }
}}
/>
Note that you don't even have to put it inside ThemeProvider, GlobalStyles uses the defaultTheme if not provided any:
return (
<>
<GlobalStyles
styles={(theme) => ({
h1: { color: theme.palette.primary.main },
h2: { color: "green" },
body: { backgroundColor: "lightpink" }
})}
/>
<h1>This is a h1 element</h1>
<h2>This is a h2 element</h2>
</>
);
Live Demo

You can actually write global styles with material UI:
const useStyles = makeStyles((theme) => ({
'#global': {
'.MuiPickersSlideTransition-transitionContainer.MuiPickersCalendarHeader-transitionContainer': {
order: -1,
},
'.MuiTypography-root.MuiTypography-body1.MuiTypography-alignCenter': {
fontWeight: 'bold',
}
}
}));

Global Styles with Material UI & React
// 1. GlobalStyles.js
import { createStyles, makeStyles } from '#material-ui/core';
const useStyles = makeStyles(() =>
createStyles({
'#global': {
html: {
'-webkit-font-smoothing': 'antialiased',
'-moz-osx-font-smoothing': 'grayscale',
height: '100%',
width: '100%'
},
'*, *::before, *::after': {
boxSizing: 'inherit'
},
body: {
height: '100%',
width: '100%'
},
'#root': {
height: '100%',
width: '100%'
}
}
})
);
const GlobalStyles = () => {
useStyles();
return null;
};
export default GlobalStyles;
** Then Use it in App.js like below**
// 2. App.js
import React from 'react';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import { Router } from 'react-router-dom';
import { NavBar, Routes, GlobalStyles, Routes } from '../';
const theme = createMuiTheme({
palette: {
primary: {
main: 'blue'
}
}
});
const App = () => {
return (
<MuiThemeProvider theme={theme}>
<Router>
<NavBar />
<GlobalStyles />
<Routes />
</Router>
</MuiThemeProvider>
);
};
export default App;
This Works for me with my react project.

For global styles you can use it like shown below.
This is the best implementation that has worked for me.
const theme = createMuiTheme({
overrides: {
MuiCssBaseline: {
'#global': {
html: {
WebkitFontSmoothing: 'auto',
},
},
},
},
});
// ...
return (
<ThemeProvider theme={theme}>
<CssBaseline />
{children}
</ThemeProvider>
);
For more reference: Global CSS

In my experience, using MuiThemeProvider and createMuiTheme have worked wonderfully. However, I am using Material-UI version 3.9.2.
MuiThemeProvider should wrap around your entire application. All you need to do in all of your components would be to instead of passing your styles object to with styles, pass a function that passes in the theme.
Ex:
import React from 'react';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import {NavBar, Routes} from '../'
const theme = createMuiTheme({
palette: {
primary: {
main: 'red'
},
},
/* whatever else you want to add here */
});
class App extends Component {
render() {
return (
<MuiThemeProvider theme={theme}>
<NavBar />
<Routes />
</MuiThemeProvider>
)
}
then in navbar let's say:
import React from 'react';
import { withStyles } from '#material-ui/core';
const styles = theme => ({
root: {
color: theme.palette.primary.main,,
}
})
const NavBar = ({classes}) => {
return <div className={classes.root}>navigation</div>
}
export default withStyles(styles)(NavBar);
Hope that helps, works very well for me!

Related

Custom React Navigation Dark and Light themes not being applied to child components

Im having problems with passing custom light or dark themes from react-navigation. The new defined object items of a theme are not overwritten as shown below. My goal is to use custom dark and light themes that i can use in every component. But now only the default values are showing not the custom ones.
App.js:
import React from 'react';
import { AppearanceProvider } from 'react-native-appearance';
import Navigation from './components/Navigation.js';
const App = () => {
return (
<AppearanceProvider>
<Navigation />
</AppearanceProvider>
);
};
export default App;
Inside the Navigation component im using the navigationContainer to pass the theme prop based on light or dark theme.
Navigation.js:
import React, { useState } from 'react';
import { NavigationContainer, DefaultTheme, DarkTheme } from '#react-navigation/native';
import { createStackNavigator, HeaderBackButton } from '#react-navigation/stack';
import Login from './screens/Login.js';
const Navigation = props => {
//CUSTOM THEMES
const MyTheme = {
...DefaultTheme,
colors: {
...DefaultTheme.colors,
background: '#b91c22',
primary: 'rgb(255, 45, 85)',
},
};
const MyThemeDark = {
...DarkTheme,
colors: {
...DarkTheme.colors,
background: 'green',
primary: 'rgb(255, 45, 85)',
},
};
//STATES
const [darkApp, setDarkApp] = useState(false);
//SET THE THEME
const appTheme = darkApp ? MyThemeDark : MyTheme;
//STYLE
const headerStyle = {
header: {
display: 'flex',
alignItems: 'center',
height: 0,
},
title: {
position: 'absolute',
top: 0,
fontWeight: '900',
fontSize: 30,
color: '#fff',
alignSelf: 'center',
},
};
return (
<NavigationContainer theme={appTheme}>
<Stack.Navigator headerMode='screen'>
<Stack.Screen
name='Login'
component={Login}
options={{
headerStyle: headerStyle.header,
headerTitleStyle: headerStyle.title,
title: 'Login',
}}
/>
</Stact.Navigator>
</NavigationContainer>
);
};
export default Navigation;
Then for example i want to get the overwritten background color in a button component, so i use the useTheme option from react-navigation. But the background color is not overwritten the default one. I was expected that the background would have the color #b91c22 but instead it is showing rgb(240, 240, 240), what im i doing wrong here?
Example Button.js:
import React from 'react';
import { TouchableOpacity, Text, View } from 'react-native';
import { useColorScheme } from 'react-native-appearance';
import { useTheme } from '#react-navigation/native';
const Button = props => {
//PROPS
const {} = props;
//THEMES
const { colors } = useTheme();
let scheme = useColorScheme();
return (
<TouchableOpacity style={{ backgroundColor: colors.background }}>
<Text>Im Button</Text>
</TouchableOpacity>
);
};
export default Button;

'Undefined' when attempting to pass MaterialUI theme props to styled components

I'm attempting to access my Material-UI theme props within a styled component, however I keep getting...
TypeError: Cannot read property 'primary' of undefined or similar errors in the browser.
Here is my custom theme (index.ts)
import { createMuiTheme } from "#material-ui/core";
import { blue } from "#material-ui/core/colors";
const theme = createMuiTheme({
palette: {
primary: {
main: blue[800],
contrastText: "#FFF"
},
secondary: {
main: blue[600],
contrastText: "#FFF"
}
},
typography : {
fontFamily: [
"Nunito",
"Roboto",
].join(",")
}
});
export default theme;
Here is where my theme wraps my application in the App.tsx
// Other imports
import theme from "../../app/theme/index";
const App: React.FC = () => {
return (
<>
<StylesProvider injectFirst>
<ThemeProvider theme={theme}>
// Routing stuff
</ThemeProvider>
</StylesProvider>
</>
);
};
export default App;
And here is where I am attempting to use my styled component
import { ListItem } from "#material-ui/core";
import React from "react";
import styled from "styled-components";
const Brand = styled(ListItem)`
background-color: ${props => props.theme.palette.primary.dark},
padding: ${props => props.theme.spacing(1)},
font-size: ${props => props.theme.typography.h6.fontSize},
font-weight: ${props => props.theme.typography.fontWeightMedium},
color: "white",
min-height: "64px",
padding-left: ${props => props.theme.spacing(6)}
`;
const SidebarNew: React.FC = () => {
return (
<Brand button>
// Stuff
</Brand>
);
};
export default SidebarNew;
This compiles but fails in the browser. What am I missing here?
If I use material-ui's built in styled like below, it appears to work, however I would prefer to use styled-components directly
const Brand = styled(ListItem)(({ theme }) => ({
backgroundColor: theme.palette.primary.dark,
padding: theme.spacing(1),
fontSize: theme.typography.h6.fontSize,
fontWeight: theme.typography.fontWeightMedium,
color: "white",
minHeight: 64,
paddingLeft: theme.spacing(6)
}));
You need to use ThemeProvider from styled-components (SCThemeProvider in the example below) in addition to the Material-UI ThemeProvider; otherwise styled-components won't know about your theme.
Here is a working example:
import {
createMuiTheme,
StylesProvider,
ThemeProvider
} from "#material-ui/core";
import { ThemeProvider as SCThemeProvider } from "styled-components";
import * as React from "react";
import Dashboard from "./Dashboard";
import "./styles.css";
const theme = createMuiTheme({
palette: {
primary: {
main: "#1a1aff",
contrastText: "#FFF"
},
secondary: {
main: "#ff3333",
contrastText: "#FFF"
}
},
typography: {
h5: {
fontSize: "10px"
}
}
});
export default function App() {
return (
<StylesProvider injectFirst>
<ThemeProvider theme={theme}>
<SCThemeProvider theme={theme}>
<Dashboard />
</SCThemeProvider>
</ThemeProvider>
</StylesProvider>
);
}

React material how to theming body color

Is there a way to change the default body color
using CssBaseline in react material?
I don't want using like
typography: {
h2: {
color: "red",
},
},
but globally is this possible ?
I don't find any example.
UPDATE
It works with
const theme = createMuiTheme({
overrides: {
MuiCssBaseline: {
"#global": {
body: {
backgroundColor: "red",
color: "green",
},
},
},
},
});
Watch out you must have CssBaseline nested to MuiThemeProvider
<MuiThemeProvider theme={theme}>
<CssBaseline />
<App />
</MuiThemeProvider>
Here is the example of how you can set background color globally, similarly you can add typography configurations in the theme
import React from 'react';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import CssBaseline from "#material-ui/core/CssBaseline";
const theme = createMuiTheme({
palette: {
background: {
default: "#303030"
}
}
});
function App() {
return (
<MuiThemeProvider theme={theme}>
<React.Fragment>
<CssBaseline />
<Your Componennt />
</React.Fragment>
</MuiThemeProvider>
);
}
Hope this works for you :)
Yes, brother, you can override everything globally in material UI, Here I write some Inputs, Buttons, Labels globally.
import React, { Component } from "react";
import { Box, CssBaseline } from "#material-ui/core";
import { createMuiTheme, MuiThemeProvider } from "#material-ui/core/styles";
import App from "../App";
class Layout extends Component {
/**
* Render
*/
render() {
const theme = createMuiTheme({
palette: {
secondary: {
light: "green",
main: "green",
dark: "green",
boxShadow: "none",
},
background: {
default: "red",
},
},
});
return (
<MuiThemeProvider theme={theme}>
<CssBaseline />
<Box component="div">
<App />
</Box>
</MuiThemeProvider>
);
}
}
export default Layout;

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>

Resources