How do I access Material-ui's theme in Styled Component
Asked Answered
P

11

26

I'm using CRA with Material-ui and Styled Components type of styling. When building my CSS I want to access Material-ui's default theme.

part of package.json:

  "dependencies": {
    "react": "^16.8.6",
    "react-dom": "^16.8.6",
    "react-scripts": "3.0.1",
    "@material-ui/core": "^4.2.1",
    "@material-ui/icons": "^4.2.1",
    "@material-ui/styles": "^4.2.1",
    "styled-components": "^4.3.2"
  }

When I try the below theme exists on props but is an empty object.

StyledApp.js:

import styled from "styled-components";
import Button from "@material-ui/core/Button";

export const StyledButtonUsingTheme = styled(Button)`
  //Below will give "Cannot read property 'error' of undefined" 
  background-color: ${props => props.theme.palette.error.light};
`;

App.js:

import React from "react";
import "./App.css";

import { StylesProvider, ThemeProvider } from "@material-ui/styles";
import { createMuiTheme } from "@material-ui/core/styles";

import { StyledButtonUsingTheme } from "./StyledApp";

function App() {
  const defaultTheme = createMuiTheme();

  window.console.log("Default theme passing to ThemeProvider", defaultTheme);

  return (
    <StylesProvider injectFirst>
      <ThemeProvider theme={defaultTheme}>
        <div className="App">
          <StyledButtonUsingTheme variant="outlined">
            Styled Button Using Theme
          </StyledButtonUsingTheme>
        </div>
      </ThemeProvider>
    </StylesProvider>
  );
}

export default App;

The console.log in App.js shows the whole theme object, and that's what I pass to ThemesProvider. Interestingly props.theme is there! but sadly with no values.

Propulsion answered 19/7, 2019 at 8:38 Comment(7)
I have a workaround: Inside StyledApp.js to use: background-color: ${props => createMuiTheme().palette.error.light}; ...but it feels like a hack to call createMuiTheme() each time I want to use the themePropulsion
But you are no passing the theme to StyledButtonUsingTheme in any way. I don't see how this can workAssemblyman
I'm expecting theme to be added to every child component of <ThemeProvider>. It is actually added but empty. Note that theme is not undefined, it is an empty object, where did it come from?Propulsion
Watch out: you are importing styled from the wrong place. import { styled } from '@material-ui/styles';Assemblyman
codesandbox.io/s/pedantic-kapitsa-l3bn3 copied and pasted example from material-ui.com/guides/interoperability/#themeprovider.Preposterous
@Assemblyman if I do that then no styling is working. Forget the theme, no styling is respected at all. But you have a point and this got me thinking in the right direction. I obviously mixed up stuff / libraries.Propulsion
@Eric Menezes, for your example to work you need to use <StylesProvider injectFirst> just as I do in App.js. Else no styling in the StyledButtonUsingTheme.js file is respected, we get a default Material-ui button. Thanks a bunch, now that I see something that works I get new hope and energyPropulsion
S
23

As Horyd in the comment says, using the ThemeProvider from Styled-Components will give you access to the theme properties inside your styled component. But Material-UI doesn't apply that theme anymore to its own components.

The workaround I found is as ugly as it is simple: Use both Themeproviders. So Material-UI applies the theme to its components and you can access the theme in your styled components.

import { ThemeProvider } from "styled-components";
import { MuiThemeProvider,StylesProvider } from "@material-ui/core/styles";

ReactDOM.render(

  //Make sure the Material stylesheet is placed above your own 
  //styles so you can overwrite them
  <StylesProvider injectFirst> 

    //Use the theme in the ThemeProvider for Material-UI so
    //styles are applied to the Material-UI components
    <MuiThemeProvider theme={theme}>

      //Use also the ThemeProvider for Styled-Components so 
      //you can access the theme in your own css
      <ThemeProvider theme={theme}>

        //Include your app and you have acces to everything 
        <App />

      </ThemeProvider>

    </MuiThemeProvider>

  </StylesProvider>,

document.getElementById("app"));
Stereoscopic answered 19/10, 2019 at 9:9 Comment(1)
for MUI v5 use ThemeProvider instead of MuiThemeProvider and StyledEngineProvider instead of StylesProvider Nankeen
D
22

You could use withTheme :

App.js

import React from "react"
import { ThemeProvider, createMuiTheme } from "@material-ui/core/styles"
import { StyledButton } from "./StyledButton"

const App = () => {

    const theme = createMuiTheme();

    return (
        <ThemeProvider theme={theme}>
            <StyledButton />
        </ThemeProvider>
    )
}

export default App

StyledButton.js

import { styled, withTheme } from "@material-ui/core/styles"
import Button from "@material-ui/core/Button"

export const StyledButton= styled(withTheme(Button))(props => ({
  background: props.theme.palette.background.paper,
}))
Devoice answered 11/12, 2019 at 0:25 Comment(0)
P
8

Problem Solved!

The solution is to use: import { ThemeProvider } from "styled-components"; in App.js then the theme is there with all the values on the props object.

I used ThemeProvider from "@material-ui/styles" in App.js import { StylesProvider, ThemeProvider } from "@material-ui/styles"; That doesn't play well with `import styled from "styled-components" in StyledApp.js

The working two files:

App.js

import React from "react";
import "./App.css";

import { StylesProvider } from "@material-ui/styles";
import { ThemeProvider } from "styled-components";
import { createMuiTheme } from "@material-ui/core/styles";

import { StyledButtonUsingTheme } from "./StyledApp";

function App() {
  const defaultTheme = createMuiTheme();

  window.console.log("Default theme passing to ThemeProvider", defaultTheme);

  return (
    <StylesProvider injectFirst>
      <ThemeProvider theme={defaultTheme}>
        <div className="App">
          <StyledButtonUsingTheme variant="outlined">
            Styled Button Using Theme
          </StyledButtonUsingTheme>
        </div>
      </ThemeProvider>
    </StylesProvider>
  );
}

export default App;

StyledApp.js

import styled from "styled-components";
import Button from "@material-ui/core/Button";

export const StyledButtonUsingTheme = styled(Button)`
  //Below will work now!
  background-color: ${props => props.theme.palette.error.light};
`;
Propulsion answered 19/7, 2019 at 11:52 Comment(1)
I'm not sure this actually solves the issue of simultaneously providing the theme to styled-components constructed using styled and also automatically applying the theme to material-ui components such as <Button color="primary">Click me</Button>. Using your solution the second example does not adhere to the themeFoxe
F
7

The answer which used withTheme is almost complete. The last part didn't work for me, so I changed to:

import styled from 'styled-components'
import { withTheme } from "@material-ui/core/styles"

const StyledButton = withTheme(styled('h1')`
    background-color: ${props => props.theme.palette.error.light};
  `
)
Fable answered 12/7, 2020 at 10:12 Comment(0)
A
5

I did it in MUI v5 like this

import { styled as muiStyled } from '@mui/material';

const NavLinkStyle = muiStyled('div')(({ theme }) => ({
  padding: '0 10px',
  height: '100%',
  fontWeight: 'bold',
  '&:hover': {
    color: theme.palette.primary.main,
    cursor: 'pointer',
    borderRadius: '15px'
  }
}));
Aponeurosis answered 22/2, 2022 at 21:19 Comment(0)
G
3

If you want to use both ThemeProviders, first from styled-components and second from material-ui, you can use alias for one of them:

import { ThemeProvider as StyledThemeProvider} from 'styled-components';
import { ThemeProvider } from '@material-ui/core/styles';

 function App() {
  return (
    <Router>
      <ThemeProvider theme={theme}>
      <StyledThemeProvider theme={theme}>
        <Wrapper>
          <TheHeader />
          <TheContent />
          <TheFooter />
        </Wrapper>
      </StyledThemeProvider>
      </ThemeProvider>
    </Router >
  );
}

export default App;
Gilberte answered 16/2, 2021 at 9:40 Comment(0)
D
3

For MUI v5:

index.tsx

import { ThemeProvider, createTheme } from "@mui/material/styles";
import { StyledEngineProvider} from "@mui/material";
import { ThemeProvider as ScThemeProvider } from "styled-components";

import App from "components/App/App";

const theme = createTheme();

ReactDOM.render(
  <StyledEngineProvider injectFirst>
    <ThemeProvider theme={theme}>
      <ScThemeProvider theme={theme}>
        <App />
      </ScThemeProvider>
    </ThemeProvider>
  </StyledEngineProvider>,
  document.getElementById("root")
);
Devonian answered 20/2, 2022 at 11:39 Comment(0)
A
2

In my case, I found that the theme object is automatically passed in with the styled component and can be accessed via parameter as follows:

import React from 'react';
import { styled } from '@material-ui/core';

const StyledButton = styled('button')(({ theme }) => ({
    color: theme.palette.text.secondary,
}));

I have this working in MUI v4.11.0.

Abrade answered 13/10, 2021 at 23:12 Comment(1)
Thank you! This saved my butt since I have a ton of styled components and didn't want to implement withTheme all over the placeHouk
H
0

in your styled component you can use the hooks useTheme example :

import { useTheme } from "@material-ui/core"
import { styled } from "@material-ui/styles"

const theme = useTheme

  const StyledListbox = styled("ul")({
    backgroundColor: theme.palette.primary.main,
  })
Hereby answered 17/9, 2021 at 10:39 Comment(0)
S
0

While many of the answers here tell you to use multiple theme providers, that is unnecessary with MUI5 as there is a special version of their styled engine that you can install to replace the default emotion based one.

If you use that, there is also no need to wrap your styled components within useTheme or similar features. You can just "magically" access the theme by accessing the props:

import styled from 'styled-components';

const Example = styled.span`
background-color: ${props => props.theme.palette.primary.main};
`

If you get errors about undefined theme vars even after installing @mui/styled-engine-sc, you might need to update your bundler configuration.

The official docs have info for webpack and next.js as well as a config snippet you need if you're using typescript.

If you're using vite, you need to add the following to your vite.config.[j|t]s:

resolve: {
    alias: {
        '@mui/styled-engine': '@mui/styled-engine-sc',
    },
},
Sirmons answered 14/9, 2023 at 9:2 Comment(0)
S
0

Just to update for mui 5 using @mui/system

import { Box, useTheme } from "@mui/material";
import { styled } from "@mui/system";

const StyledBox = styled(Box)(({ theme }) => ({
  padding: `0 ${theme.spacing(2)}`,
  backgroundColor: theme.palette.background.default, 
  width: "100%",
}));
Suricate answered 29/5, 2024 at 19:3 Comment(0)

© 2022 - 2025 — McMap. All rights reserved.