I'm attempting to migrate from Material-UI v4 to v5 (currently in beta), but I'm not sure if I replace StylesProvider with StyledEngineProvider. There is no mention of it (see https://next.material-ui.com/guides/migration-v4/). The migration guide only mentions that StylesProvider should be imported directly from #material-ui/styles (instead of #material-ui/core/styles). It also mentions using StyledEngineProvider with the injectFirst option to take care of the css ordering. However, that is what StylesProvider does so I'm confused if I need to use StylesProvider or StyledEngineProvider.
Do I use StyledEngineProvider instead of StylesProvider? Or does it not matter because they both do the same thing? Or do I use StylesProvider if I am still using JSS and only use StyledEngineProvider if I no longer use JSS and only use Emotion? Any clarification would be appreciated.
Yep, I agree with those missing details among the two. After navigating over the repo, the difference is that StyledEngineProvider allows you to customize the current instance of StylesProvider used by MUI so by injecting Emotion first, JSS continues to work as before (<StyledEngineProvider injectFirst>).
It does matter which one you use, StylesProvider breaks the theme since you are creating your own provider without MUI's internal configuration, instead, StyledEngineProvider is only passing down a property to MUI's internal StylesProvider.
These APIs are offered as backward compatibility with V4, so it only affects JSS users. Currently, you can use both, but consider migrating your JSS styling to Emotion.
As in version 5.0.5, this should let you continue working with JSS:
import {
ThemeProvider,
StyledEngineProvider,
CssBaseline,
} from '#mui/material';
import {
createTheme
responsiveFontSizes,
} from '#mui/material/styles';
// import { StylesProvider } from '#mui/styles'; //breaks MUI theme!
const muiTheme = responsiveFontSizes(createTheme({/* your custom theme */}));
export const withJssAndCustomTheme = Component => props=>{
return (
<StyledEngineProvider injectFirst>
<ThemeProvider theme={muiTheme}>
<CssBaseline/>
<Component
{...props}
/>
</ThemeProvider>
</StyledEngineProvider>
);
};
// withJssAndCustomTheme(App) // wrap your root component
Related
I'm trying to use the ImageList component. I imported it as follows:
import { ImageList} from "#material-ui/core";
This works just fine. When I add the "variant" prop to the component, however, I start getting errors saying that I'm not matching any of the overloads. I see the variant prop used in their demos and on the API reference.
Error message
I suspect this has something to do with versioning -- I read something about ImageList being MUI v5 alpha. If that's the case I don't know what to do to fix my issue. The linker is finding the module just fine, so I don't know what's wrong.
I think I answered this myself. I was importing from the v4 version of MUI (#material-ui/core). I had to install v5 and import from here, instead:
import ImageList from '#mui/material/ImageList';
And then the variant prop worked. I was disheartened to find out that breakpoint props don't exist with this component, like they do with Grid. Instead, I had to use MUI's useMediaQuery hook to get the screen size and set the col prop accordingly.
I'm building a reusable shared-component library using material-ui which is being imported in my main app like this:
import { theme, StepperComponent } from '#company/shared-components';
import { ThemeProvider } from '#mui/material/styles';
...
return (
<ThemeProvider theme={theme}>
<StepperComponent steps={stepperTitles} />
...
)
The stepper component is in my shared-components repository which I'm npm-linking
import React from 'react';
import Step from '#mui/material/Step';
import StepLabel from '#mui/material/StepLabel';
import { StyledStepper, StyledStepConnector, StyledStepIcon } from './Stepper.styles';
const StepperComponent = ({ steps, ...props }) => {
return (
<StyledStepper {...props} connector={<StyledStepConnector />} alternativeLabel>
However, for some reason the theme isn't getting applied to this component even though I'm wrapping it in the theme provider. When I log out the theme it doesn't include the colors that are in the theme being imported to my main app.
Does anyone know why this might be happening?
I'm dealing with something similar and haven't resolved it completely, but I have a strong feeling it involves multiple React contexts being created inside material-ui and emotion/react.
The ThemeProvider only exposes the theme to the components that have access to its context - the components within your main application.
I was able to achieve the desired behavior by making sure material-ui's ThemeProvider and it's context lives in the shared component library and not the main application. I did this by exporting a simple wrapper of ThemeProvider from the component library; literally just:
interface LibraryThemeProviderProps {
children?: React.ReactNode;
theme: Theme;
}
const LibraryThemeProvider: React.FC<LibraryThemeProviderProps> = ({ children, theme }) => (
<ThemeProvider theme={theme}>
{children}
</ThemeProvider>
)
and then using LibraryThemeProvider in the place of material's ThemeProvider in the main application.
If you want the same theme applied within your main application you only need to leave the original ThemeProvider in place alongside the new LibraryThemeProvider.
I'd prefer to solve this with a single ThemeProvider but haven't been able to yet :/
Note: I am using the useTheme hook inside all my components to access the theme. useTheme will use material's default theme if it doesn't see one provided by the ThemeProvider context - all this happens under the hood.
I have encountered the same problem recently. There were likely two copies of #mui/material loaded simultaneously.
If we symlinked the #company/shared-components package into main-app directly, all the packages defined in "devDependencies" will be included even though we have included them in "peerDependencies." Thus, resulting in the following dependency tree.
main-app
└─ node_modules
├─ #mui/material <-- ThemeProvider in the main app will load this
└─ #company/shared-components
└─ node_modules
└─ #mui/material <-- Components from shared-components will load this
In my case, the solution for Webpack is to include #mui/material in resolve.alias to mimic the notion of peer dependencies, which is to use the package installed in the main-app
resolve: {
alias: {
'#mui/material': 'node_modules/#mui/material'
}
}
Related Questions
MUI ThemeProvider is not injecting theme in a compiled component
How to create a shared component library with MUI 5
nextjs + material UI
Im sure its something basic that Im overlooking, but Im trying to override mui styles with css modules. The accepted method seems to be wrapping the component tree in a StylesProvider component and passing it a injectFirst attribute. When I do this though it generates the error
*Error: Element type is invalid: expected a string (for built-in components) or*
This is using nextjs. Package.json looks like:
#emotion/cache 11.4.0
#emotion/react 11.4.0
#emotion/server 11.4.0
#emotion/styled 11.3.0
#material-ui/core 5.0.0-beta.0
clsx 1.1.1
next 11.0.1
prop-types 15.7.2
react 17.0.2
react-dom 17.0.2
There is a codesandbox where you can see the error live and examine the code. Any ideas gratefully accepted.
Material UI began using emotion as styling library in v5, so as per the migration guide, the correct component to use is StyledEngineProvider. Like so:
import * as React from 'react';
import { StyledEngineProvider } from '#material-ui/core/styles';
export default function GlobalCssPriority() {
return (
<StyledEngineProvider injectFirst>
{/* Your component tree. Now you can override Material-UI's styles. */}
</StyledEngineProvider>
);
}
I've made two Material-UI components. Both are using themes. However, the overrides on one theme are being used in the other! I'm losing my mind trying to find a solution to this. Here is the code:
This is being used for component1:
const themeForComponent1 = createMuiTheme({
overrides: {
MuiInputLabel: {
root: {
padding: '13px 0 0 13px !important'
}
},
},
})
...and it is changing stuff in component 2, which happens to also have an element with the class "MuiInputLabel-root".
(edit: If it helps at all, the component is being called in Formik using the Field element via the "as" prop. Each component is using its own ThemeProvider element.)
If you want to have two themes independent you can see the Material UI documentation here
https://material-ui.com/styles/advanced/
But in the fact yo should use two theme providers. I say you that you should use an only theme and customize your component styles using Material UI functions to do that like CSS class
I am wondering if there is a neat trick to use a component classes prop to override some CSS defined in a theme's overrides.
For instance :
If I want all Button components to have a different font-size than the default one. I can use the theme.overrides props to do so :
// this works, all Buttons text is 1.1rem
let theme = createMuiTheme({
overrides: {
MuiButton: {
label: {
"&": {fontSize: "1.1rem"}
}
}
}
})
Now if for some reason one of my button needs to have a different font-size, I was hoping using classes prop would do the job :
const useClasses = makeStyles({
smallerFontSize: {
fontSize: "0.9rem"
}
})
...
const classes = useClasses()
...
<Button
classes={{
// unfortunately this doesn't work, theme overrides is taking precedence
label: classes.smallerFontSize
}}
>
Some smaller text
</Button>
...
Since using classes prop allows us to target and override some component's CSS if default theme values have not been overridden, I find it confusing that theme overrides end up behaving somewhat differently and have a higher specificity than a one time rule.
I'd argue it kind of defeats the purpose of having a customisable theme.
But hopefully I'm missing something and your wisdom will help !
UPDATE
My mistake was to export the created theme and the makeStyles hook from the same module file.
Doing that made Mui insert theme <style> after the hook <style>.
To fix the issue and be able to use classes component props as I wanted to :
export theme and hooks from separate modules
make sure theme module has no dependency on the module exporting the hook
make sure when using ThemeProvider that it has no parent component importing the hook
I still don't quite understand why things worked before I added the overrides property on the theme object though.