71

I'm trying something very simple: building two themes for a website using Material-UI themes:

A light theme and dark one, but it does not work well: the theme is on every Material-UI react element, but the root element on the html document keeps having the same default white background.

Of course it can be changed by attacking the body with pure .css:

body {
  background-color: #222;
}

But I was looking to change it dynamically with React, I though this would work, but it does not:

import React from 'react';
import ReactDOM from 'react-dom';
import App from './App';
import { ThemeProvider } from '@material-ui/styles';
import { MuiThemeProvider, createMuiTheme } from '@material-ui/core/styles';

const themeLight = createMuiTheme({
  palette: {
    background: {
      default: "#e4f0e2"
    }
  },
});

const themeDark = createMuiTheme({
  palette: {
    background: {
      default: "#222222",
    }
  },
});

ReactDOM.render(
  <MuiThemeProvider theme = { themeDark }>
    <App />
  </MuiThemeProvider>, document.getElementById('root'));

and I'm lost here, there is no way to make this with Material-UI theme?

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
yuri
  • 3,182
  • 2
  • 16
  • 26

8 Answers8

134

CssBaseline is the component that controls this aspect. If you aren't using CssBaseline, then you are just seeing the default provided by the browser.

Here is a working v4 example (v5 example further down):

import React from "react";
import ReactDOM from "react-dom";
import CssBaseline from "@material-ui/core/CssBaseline";
import { MuiThemeProvider, createMuiTheme } from "@material-ui/core/styles";
import Button from "@material-ui/core/Button";

const themeLight = createMuiTheme({
  palette: {
    background: {
      default: "#e4f0e2"
    }
  }
});

const themeDark = createMuiTheme({
  palette: {
    background: {
      default: "#222222"
    },
    text: {
      primary: "#ffffff"
    }
  }
});

const App = () => {
  const [light, setLight] = React.useState(true);
  return (
    <MuiThemeProvider theme={light ? themeLight : themeDark}>
      <CssBaseline />
      <Button onClick={() => setLight(prev => !prev)}>Toggle Theme</Button>
    </MuiThemeProvider>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit theme body background


Below is a Material-UI v5 example. The only difference from v4 is the name change for ThemeProvider (though this name is also available in v4 in addition to MuiThemeProvider) and createTheme (instead of createMuiTheme) and using the new @mui/material package name instead of @material-ui/core.

import React from "react";
import ReactDOM from "react-dom";
import CssBaseline from "@mui/material/CssBaseline";
import { ThemeProvider, createTheme } from "@mui/material/styles";
import Button from "@mui/material/Button";

const themeLight = createTheme({
  palette: {
    background: {
      default: "#e4f0e2"
    }
  }
});

const themeDark = createTheme({
  palette: {
    background: {
      default: "#222222"
    },
    text: {
      primary: "#ffffff"
    }
  }
});

const App = () => {
  const [light, setLight] = React.useState(true);
  return (
    <ThemeProvider theme={light ? themeLight : themeDark}>
      <CssBaseline />
      <Button onClick={() => setLight((prev) => !prev)}>Toggle Theme</Button>
    </ThemeProvider>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit theme body background

Ryan Cogswell
  • 75,046
  • 9
  • 218
  • 198
8

In MUI v5, you can also use GlobalStyles component to add the styles to the body element:

<GlobalStyles
  styles={{
    body: { backgroundColor: "lightyellow" }
  }}
/>

Edit 59145165/change-root-background-color-with-material-ui-theme

NearHuscarl
  • 66,950
  • 18
  • 261
  • 230
6

On top of @NearHuscarl 's answer, importing the GlobalStyles after the CSSBaseLine, will retain the page defaults (like margin: 0, etc.,) still able to customize root-level / global styles. For eg,

import { Component } from "react";
import { Button, CssBaseline, GlobalStyles } from "@mui/material";
import { ThemeProvider, createTheme } from "@mui/material/styles";

export class App extends Component {
  render() {
    const theme = createTheme({
      palette: {
        mode: "dark",
        primary: {
          main: "#ff0000",
          contrastText: "#fff",
        },
        secondary: {
          main: green[500],
        },
      },
    });
    return (
      <ThemeProvider theme={theme}>
        <CssBaseline />
        <GlobalStyles
          styles={{
            body: { backgroundColor: "cyan" },
          }}
        />
        <Button color="primary" variant="contained">
          Button
        </Button>
      </ThemeProvider>
    );
  }
}

export default App;

(I'm just using class component out of habit )

Full example with nested themes MUI Theme toggle

MrSrv7
  • 585
  • 8
  • 22
  • My problem were importing wrong package ThemeProvider module, I was earlier picking `import ThemeProvider from "@mui/styles/ThemeProvider"`, now I correct to `import { ThemeProvider } from "@mui/material/styles";` and working fine, thanks. – ArifMustafa Dec 28 '21 at 05:32
3

My use case, I only wanted to change background-color of body from within a React component, not the entire theme. Used a global override.

TL;DR code:

// other imports ...
import { makeStyles } from "@material-ui/core/styles";

const useStyles = makeStyles((theme) => ({
  '@global':{
      body:{
        backgroundColor:"#382E7E"
      }
  },
  otherstyles:{
    //  other styles ....
  },

}));


// React component, etc ...
Hillsie
  • 607
  • 1
  • 6
  • 15
1

MUI Version 5

import { createTheme } from "@mui/material/styles";

const themeX = createMuiTheme({
  palette: {
    mode: "dark",
  }
});

MUI Version 4

import { createMuiTheme } from '@material-ui/core/styles';

const themeX = createMuiTheme({
   palette: {
     type: "dark",
   }
 });

just as simple that, changing the pallets type to dark by default its set to light. This will also help in custom color for other components like typography, icon etc

Chukwuemeka Maduekwe
  • 6,687
  • 5
  • 44
  • 67
  • 1
    for mui 5 this is now mode, not type, and its just createTheme. This should really just say its mui 4 specific. – Christo Feb 02 '23 at 19:32
  • You need to render the `CssBaseline` component too in order for `mode` to change the `background-color`. – Ini Feb 14 '23 at 10:59
0

ReactDOM doesn't replace the targeted element. I haven't worked with material ui personally. However, if you put the background color you want into your App state as something like 'currentRootColor', then in your App component's render function you could put:

render() {
    document.body.style.backgroundColor = this.state.currentRootColor;

    ...the rest of your App render code
}

This would set the body's background color and if you change 'this.state.currentRootColor', then your App component would re-render with the new background color.

However if you dont already have a < body > tag in your document you would need to add one.

Andrew Mellor
  • 218
  • 1
  • 8
0

In MUI v5, this seem to work for me. I used it to apply specific styles only to HomePage (overwrite default styles).

pages/HomePage.js

...

import GlobalStyles from '@mui/material/GlobalStyles';
// or
import { GlobalStyles } from '@mui/material';

Note: It is a good practice to hoist the <GlobalStyles /> to a static constant, to avoid rerendering. This will ensure that the tag generated would not recalculate on each render.

  const homePageStyles = (
    <GlobalStyles
      styles={{
        body: { backgroundColor: 'cyan' },
        '.MuiTypography-root': {
          color: 'red',
        },
      }}
    />
  );

...

return (
    <>
      {homePageStyles}
      <MyComponents />
    </>
);

....

More:

https://mui.com/material-ui/customization/how-to-customize/

https://mui.com/material-ui/api/global-styles/

atazmin
  • 4,757
  • 1
  • 32
  • 23
0

All the above answers did not work for me, why?? I don't know.

I covered all of my components with ScopedCssBaseline and mui will apply the palette style only to the children.

Below is my answer,

import React from "react";
import ReactDOM from "react-dom";
import { ScopedCssBaseline, Button } from "@mui/material";
import { ThemeProvider, createTheme } from "@mui/material/styles";

const themeLight = createTheme({
  palette: {
    background: {
      default: "#fff"
    },
    text: {
      default: "#000"
    }
  }
});

const themeDark = createTheme({
  palette: {
    background: {
      default: "#000"
    },
    text: {
      primary: "#fff"
    }
  }
});

const App = () => {
  const [light, setLight] = React.useState(true);

  return (
    <ThemeProvider theme={light ? themeLight : themeDark}>
      <ScopedCssBaseline enableColorScheme>
        <Button onClick={() => setLight((prev) => !prev)}>Toggle Theme</Button>
      </ScopedCssBaseline>
    </ThemeProvider>
  );
};

const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Edit theme body scoped background

Nisharg Shah
  • 16,638
  • 10
  • 62
  • 73