wojjas
wojjas

Reputation: 1096

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

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.

Upvotes: 27

Views: 36591

Answers (11)

balui
balui

Reputation: 156

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%",
}));

Upvotes: 0

DysphoricUnicorn
DysphoricUnicorn

Reputation: 552

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',
    },
},

Upvotes: 0

greatertomi
greatertomi

Reputation: 988

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'
  }
}));

Upvotes: 5

thisismydesign
thisismydesign

Reputation: 25182

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")
);

Upvotes: 4

Kurt Vangraefschepe
Kurt Vangraefschepe

Reputation: 130

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.

Upvotes: 2

idembele70
idembele70

Reputation: 139

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,
  })

Upvotes: 0

blazej
blazej

Reputation: 61

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;

Upvotes: 3

ramigg
ramigg

Reputation: 1305

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};
  `
)

Upvotes: 7

Into Numbers
Into Numbers

Reputation: 973

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,
}))

Upvotes: 22

NLAnaconda
NLAnaconda

Reputation: 1605

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"));

Upvotes: 23

wojjas
wojjas

Reputation: 1096

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};
`;

Upvotes: 8

Related Questions