Is it possible to override material-ui components default props? - reactjs

Lets say I want every Button component from material-ui to have default props of variant="contained" color="secondary". Is this possible?

The documentation for this is here: https://material-ui.com/customization/globals/#default-props
Here is an example of how to do this in v4 (v5 example further down):
import React from "react";
import ReactDOM from "react-dom";
import {createMuiTheme, MuiThemeProvider, Button} from "#material-ui/core";
const theme = createMuiTheme({
props: {
MuiButton: {
variant: "contained",
color: "secondary"
}
}
});
function App() {
return (
<MuiThemeProvider theme={theme}>
<Button>Props defaulted</Button>
</MuiThemeProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
Here's a similar example, but with the theme structure for v5 of Material-UI:
import React from "react";
import ReactDOM from "react-dom";
import Button from "#mui/material/Button";
import { createTheme, ThemeProvider } from "#mui/material/styles";
const theme = createTheme({
components: {
MuiButton: {
defaultProps: {
variant: "contained",
color: "secondary"
}
}
}
});
function App() {
return (
<ThemeProvider theme={theme}>
<Button>Props defaulted</Button>
</ThemeProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Create your own component <MyButton /> which renders a <Button> component from material-ui. Style the component as you wish and use MyButton in your code.

If I understood the question correctly, you have to set that when you instantiate your theme :
const theme = createMuiTheme({
overrides: {
MuiButton: {
root: {
fontSize: '1rem',
},
},
},
});

Related

cannot use custom Material UI theme [duplicate]

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?
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);
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);
In MUI v5, you can also use GlobalStyles component to add the styles to the body element:
<GlobalStyles
styles={{
body: { backgroundColor: "lightyellow" }
}}
/>
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
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 ...
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
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.
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/
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);

How to theme Material UI inside Storybook

Currently this is what I am doing, passing a ThemeProvider above my component file:
import React from 'react';
import { ThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import MUIButton from '#material-ui/core/Button';
const theme = createMuiTheme({
palette: {
primary: {
main: "#ff0000"
}
},
typography: {
fontFamily: 'Nunito Sans, sans-serif',
button: {
textTransform: 'none'
}
},
shape: {
borderRadius: 3
}
})
export default ({ variant, children }) => {
return (
<ThemeProvider theme={theme}>
<MUIButton
color="primary"
variant={variant}
>
{children}
</MUIButton>
</ThemeProvider>
)
}
I am trying to figure out how I can do this at a global level in Storybook. This is the first component I have built out called Button. So I want to be able to have the theme in an external file, and have the ThemeProvider coming in at a higher level so I don't have to wrap each component. Hope that makes sense, and if anyone has any ideas.
First, I suggest you to move your theme into a separate file (such as src/stylesheet so you can access it from different files (your global App component and your storybook preview file).
// src/stylesheet.ts
import { createMuiTheme } from '#material-ui/core/styles';
export const muiTheme = createMuiTheme({
palette: {
primary: {
main: "#ff0000"
}
},
typography: {
fontFamily: 'Nunito Sans, sans-serif',
button: {
textTransform: 'none'
}
},
shape: {
borderRadius: 3
}
})
Then, you need to setup your storybook the same way you set up your react app. To do so, try to create a file called:
.storybook/preview.js and put this inside of it:
// .storybook/preview.js
import React from 'react';
import { addDecorator } from '#storybook/react';
import { ThemeProvider } from '#material-ui/core/styles';
import { muiTheme } from '../src/stylesheet';
addDecorator((story) => (
<ThemeProvider theme={muiTheme}>{story()}</ThemeProvider>
));
It will wrap all your stories inside of the ThemeProvider.
In your app, you can can also have a global App component which will encapsulate the whole app within the ThemeProvider
More help:
https://storybook.js.org/docs/basics/writing-stories/
This is the only solution that worked for me with MUI v5:
// .storybook/main.js
const path = require('path');
const toPath = (filePath) => path.join(process.cwd(), filePath);
module.exports = {
webpackFinal: async (config) => {
return {
...config,
resolve: {
...config.resolve,
alias: {
...config.resolve.alias,
'#emotion/core': toPath('node_modules/#emotion/react'),
'emotion-theming': toPath('node_modules/#emotion/react'),
},
},
};
},
};
// .storybook/preview.js
import { ThemeProvider, createTheme } from '#mui/material/styles';
import { ThemeProvider as Emotion10ThemeProvider } from 'emotion-theming';
const defaultTheme = createTheme(); // or your custom theme
const withThemeProvider = (Story, context) => {
return (
<Emotion10ThemeProvider theme={defaultTheme}>
<ThemeProvider theme={defaultTheme}>
<Story {...context} />
</ThemeProvider>
</Emotion10ThemeProvider>
);
};
export const decorators = [withThemeProvider];
// ...other storybook exports
It's from the MUI migration guide but it took me unnecessarily long to find so I want to share it here:
https://mui.com/guides/migration-v4/
For Storybook 6.3+ it should look like the following
Update preview.js with:
import React from 'react';
import { theme } from './src/theme'; // whereever you have defined your material ui theme
export const decorators = [
Story => (
<ThemeProvider theme={theme}>
<Story />
</ThemeProvider>
),
];
More details on how to decorate your stories here:
https://storybook.js.org/docs/react/writing-stories/decorators
And creating your theme for Material UI here:
https://material-ui.com/customization/theming/
This works for me with react-router and mui V5
my preview.js =>
import React from 'react';
import { addDecorator } from '#storybook/react';
import { MemoryRouter } from 'react-router';
import { ThemeProvider } from '#mui/material/styles';
import { ThemeProvider as Emotion10ThemeProvider } from 'emotion-theming';
import customTheme from '../src/theme/index';
addDecorator((story) => (
<Emotion10ThemeProvider theme={customTheme}>
<ThemeProvider theme={customTheme}>
<MemoryRouter initialEntries={['/']}>{story()}</MemoryRouter>
</ThemeProvider>
</Emotion10ThemeProvider>
));
export const parameters = {
actions: { argTypesRegex: '^on[A-Z].*' },
controls: {
matchers: {
color: /(background|color)$/i,
date: /Date$/,
},
},
};
I believe this is the most up to date answer... and the cleanest, updating path/to/your/Theme with your path
// .storybook/preview.js
import React from 'react';
import { ThemeProvider } from '#mui/system';
import { Theme } from 'path/to/your/Theme';
export const decorators = [
(Story) => (
<ThemeProvider theme={Theme}>
<Story />
</ThemeProvider>
),
];

Change root background color with Material-UI theme

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?
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);
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);
In MUI v5, you can also use GlobalStyles component to add the styles to the body element:
<GlobalStyles
styles={{
body: { backgroundColor: "lightyellow" }
}}
/>
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
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 ...
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
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.
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/
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);

How do I style a material-ui Icon which was passed as prop

I'm writing a custom Material UI React component which I want to pass an Icon into as a prop. However I want to style the icon when I get it and make it a minimum width and height.
Here's a simplified version of what I'm trying to do. I want to apply the iconStyle to the icon passed in as props.statusImage but can't figure out how.
import PropTypes from "prop-types";
import { makeStyles } from "#material-ui/styles";
const useStyles = makeStyles({
iconStyle: {
minWidth: 100,
minHeight: 100
}
});
function MyComponentWithIconProps(props) {
const styles = useStyles();
return <div>{props.statusImage}</div>;
}
MyComponentWithIconProps.propTypes = {
statusImage: PropTypes.element
};
export default MyComponentWithIconProps;
I use the component like this
import {Done} from "#material-ui/icons";
<MyComponentWithIconProps statusImage={<Done/>}
Code Sandbox : https://codesandbox.io/s/jovial-fermi-dmb0p
I've also tried wrapping the supplied Icon in another Icon element and attempting to style that. However that didn't work and seems sort of 'hacky' anyway.
There are three main alternatives:
Pass in the element type of the icon rather than an element (e.g. Done instead of <Done/>) and then add the className as you render the element (this is the approach in Fraction's answer).
Clone the element in order to add the className prop to it.
Put a class on the parent element and target the appropriate child type (e.g. svg).
Approach 1:
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Done } from "#material-ui/icons";
import MyComponentWithIconProps from "./MyComponentWithIconProps";
function App() {
return (
<div className="App">
<MyComponentWithIconProps statusImage={Done} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
MyComponentWithIconProps.js
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "#material-ui/styles";
const useStyles = makeStyles({
iconStyle: {
minWidth: 100,
minHeight: 100
}
});
function MyComponentWithIconProps(props) {
const styles = useStyles();
const StatusImage = props.statusImage;
return (
<div>
<StatusImage className={styles.iconStyle} />
</div>
);
}
MyComponentWithIconProps.propTypes = {
statusImage: PropTypes.element
};
export default MyComponentWithIconProps;
Approach 2:
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Done } from "#material-ui/icons";
import MyComponentWithIconProps from "./MyComponentWithIconProps";
function App() {
return (
<div className="App">
<MyComponentWithIconProps statusImage={<Done />} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
MyComponentWithIconProps.js
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "#material-ui/styles";
import clsx from "clsx";
const useStyles = makeStyles({
iconStyle: {
minWidth: 100,
minHeight: 100
}
});
function MyComponentWithIconProps(props) {
const styles = useStyles();
const styledImage = React.cloneElement(props.statusImage, {
// Using clsx to combine the new class name with any existing ones that may already be on the element
className: clsx(styles.iconStyle, props.statusImage.className)
});
return <div>{styledImage}</div>;
}
MyComponentWithIconProps.propTypes = {
statusImage: PropTypes.element
};
export default MyComponentWithIconProps;
Approach 3:
index.js
import React from "react";
import ReactDOM from "react-dom";
import { Done } from "#material-ui/icons";
import MyComponentWithIconProps from "./MyComponentWithIconProps";
function App() {
return (
<div className="App">
<MyComponentWithIconProps statusImage={<Done />} />
</div>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);
MyComponentWithIconProps.js
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "#material-ui/styles";
const useStyles = makeStyles({
iconStyle: {
"& > svg": {
minWidth: 100,
minHeight: 100
}
}
});
function MyComponentWithIconProps(props) {
const styles = useStyles();
return <div className={styles.iconStyle}>{props.statusImage}</div>;
}
MyComponentWithIconProps.propTypes = {
statusImage: PropTypes.element
};
export default MyComponentWithIconProps;
Pass the icon like this:
<MyComponentWithIconProps statusImage={Done} />
then use it as follows:
return <div><props.statusImage className={styles.iconStyle} /></div>;
I would do like this:
import React from "react";
import PropTypes from "prop-types";
import { makeStyles } from "#material-ui/styles";
const useStyles = makeStyles({
iconStyle: {
minWidth: 100,
minHeight: 100,
color: "red"
}
});
function MyComponentWithIconProps(props) {
const styles = useStyles();
return <div className={styles.iconStyle}>{props.statusImage}</div>;
}
MyComponentWithIconProps.propTypes = {
statusImage: PropTypes.element
};
export default MyComponentWithIconProps;
In my case this is work :
Index.js
import {Done} from "#material-ui/icons";
<MyComponentWithIconProps icon={<Done {/*with some props*/}/>}/>
MyComponentWithIconProps.js
return (<div >{props.icon}</div>);
CodeSanbox

React & Material-UI: Theme undefined in createStyles() with TypeScript

I'm learning React with TypeScript and using the Material UI framework for the frontend. I try to get the media queries working, but I got an error:
Uncaught TypeError: Cannot read property 'up' of undefined
at styles (webpack-internal:///./app/components/navigation/Toolbar/index.tsx:59)
This is the corresponding code:
const styles = ({breakpoints}: Theme) => createStyles( {
grow: {
flexGrow: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
},
sectionDesktop: {
display: 'none',
[breakpoints.up('md')]: {
display: 'flex'
}
},
sectionMobile: {
display: 'flex'
},
})
The styles are passed to the component with:
export default withStyles(styles)(Toolbar)
I read that it is not required to create a custom theme as the default one will be passed automatically to the functions. However, the breakpoints property of theme is undefined which cause a blank page.
Thanks for your help
Edit
Here is the code of the component which will still produce the problem without any other components.
import * as React from 'react'
import {
Theme
} from '#material-ui/core'
import {
createStyles,
WithStyles,
withStyles
} from '#material-ui/styles'
// import Drawer from '../Drawer'
const styles = ({breakpoints}: Theme) => createStyles( {
grow: {
flexGrow: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
},
sectionDesktop: {
display: 'none',
[breakpoints.up('md')]: {
display: 'flex'
}
},
sectionMobile: {
display: 'flex'
},
})
namespace Toolbar {
interface Props {
}
export interface State {
isOpen : boolean
mobileMoreAnchorEl? : EventTarget & HTMLElement
}
export type AllProps = Props & WithStyles<typeof styles>
}
class Toolbar extends React.Component<Toolbar.AllProps, Toolbar.State> {
constructor(props: Toolbar.AllProps, context?: any) {
super(props, context);
this.state = { isOpen: false, mobileMoreAnchorEl: undefined}
}
render() {
const { classes } = this.props
// const { isOpen } = this.state
return(
<React.Fragment>
<div className={classes.sectionDesktop}>
Hello
</div>
<div className={classes.sectionMobile}>
World
</div>
</React.Fragment>
)
}
}
export default withStyles(styles)(Toolbar)
The main.tsx (a.k.a index.js) looks like this:
import * as React from 'react';
import * as ReactDOM from 'react-dom';
import { Provider } from 'react-redux';
import { createBrowserHistory } from 'history';
import { configureStore } from 'app/store';
import { Router } from 'react-router';
import { MuiThemeProvider, createMuiTheme } from '#material-ui/core/styles';
import { App } from './app';
// prepare store
const history = createBrowserHistory()
const store = configureStore()
const theme = createMuiTheme()
ReactDOM.render(
<Provider store={store}>
<Router history={history}>
<MuiThemeProvider theme={theme} >
<App />
</MuiThemeProvider>
</Router>
</Provider>,
document.getElementById('root')
);
So, adding MuiThemeProvider does not help.
UPDATE
At the time when this answer was first written, #material-ui/styles was unstable. It is not anymore (as of v4), but it is still generally best to import from #material-ui/core/styles since the default theme will not be available when importing from #material-ui/styles.
You can read here that #material-ui/styles is unstable (alpha version).
You'll notice in my CodeSandbox that I am using:
import { withStyles, createStyles } from "#material-ui/core/styles";
instead of importing these from #material-ui/styles. When I use the same import as you, I am able to reproduce your problem.
UPDATE for v5
In v5, usage of makeStyles and withStyles is deprecated and they were removed from #material-ui/core and are only accessible via #material-ui/styles. In v5, makeStyles and withStyles do not have access to the theme unless you provide access via the ThemeProvider (and for that to work you should be using the latest v5 versions of both #material-ui/core and #material-ui/styles). Below is a working v5 example.
import React from "react";
import ReactDOM from "react-dom";
import { createTheme, ThemeProvider } from "#material-ui/core/styles";
import { withStyles } from "#material-ui/styles";
const styles = ({ breakpoints }) => ({
grow: {
flexGrow: 1
},
menuButton: {
marginLeft: -12,
marginRight: 20
},
sectionDesktop: {
display: "none",
[breakpoints.up("md")]: {
display: "flex"
}
},
sectionMobile: {
display: "flex"
}
});
const MyToolbar = (props) => {
return (
<>
<div className={props.classes.sectionDesktop}>Section Desktop</div>
{props.children}
<div className={props.classes.sectionMobile}>Section Mobile</div>
</>
);
};
const theme = createTheme();
const StyledToolbar = withStyles(styles)(MyToolbar);
function App() {
return (
<ThemeProvider theme={theme}>
<StyledToolbar>
<div>Hello</div>
<div>CodeSandbox</div>
</StyledToolbar>
</ThemeProvider>
);
}
const rootElement = document.getElementById("root");
ReactDOM.render(<App />, rootElement);

Resources