24

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.

wojjas
  • 1,046
  • 1
  • 9
  • 21
  • 1
    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 theme – wojjas Jul 19 '19 at 09:07
  • But you are no passing the `theme` to `StyledButtonUsingTheme` in any way. I don't see how this can work – keul Jul 19 '19 at 09:09
  • I'm expecting theme to be added to every child component of . It is actually added but empty. Note that theme is not undefined, it is an empty object, where did it come from? – wojjas Jul 19 '19 at 09:45
  • Watch out: you are importing `styled` from the wrong place. `import { styled } from '@material-ui/styles';` – keul Jul 19 '19 at 09:50
  • 1
    https://codesandbox.io/s/pedantic-kapitsa-l3bn3 copied and pasted example from https://material-ui.com/guides/interoperability/#themeprovider. – erich Jul 19 '19 at 10:06
  • @keul 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. – wojjas Jul 19 '19 at 12:10
  • @Eric Menezes, for your example to work you need to use 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 energy – wojjas Jul 19 '19 at 12:11

9 Answers9

21

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,
}))
Into Numbers
  • 923
  • 11
  • 19
20

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"));
NLAnaconda
  • 1,534
  • 3
  • 14
  • 38
  • 1
    for MUI v5 use `ThemeProvider` instead of `MuiThemeProvider` and `StyledEngineProvider` instead of `StylesProvider ` – henk Aug 12 '21 at 07:48
7

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};
`;
wojjas
  • 1,046
  • 1
  • 9
  • 21
  • 2
    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 ``. Using your solution the second example does not adhere to the theme – horyd Sep 12 '19 at 22:49
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};
  `
)
ramigg
  • 1,287
  • 1
  • 15
  • 16
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'
  }
}));
greatertomi
  • 479
  • 9
  • 10
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")
);
thisismydesign
  • 21,553
  • 9
  • 123
  • 126
2

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;
blazej
  • 31
  • 3
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.

  • 2
    Thank you! This saved my butt since I have a ton of styled components and didn't want to implement `withTheme` all over the place – Daniel W. Jan 05 '22 at 22:31
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,
  })
idembele70
  • 81
  • 4