MUI v5 + styled() + ListItemButton: property 'to'/'component' does not exist - reactjs

In the migration from MUI v4 to v5 I'm hitting this road block: in v4 I've used makeStyles(), but now want to fully migrate to styled(): I can't get Typescript to accept a styled(ListItemButton)(...) with to= and component= properties.
I've seen and read MUI's guide on Wrapping components which actually made things even less clear to me; admittedly, I'm neither a Typescript nor MUI wizard. My confusion is fueled by the guide's examples being incomplete, such as obviously lacking some non-obviously imports which seem to need imported symbol renaming, so automatic completion won't work or turn up multiple candidates.
This is a minimized example triggering the Typescript error, see below for codesandbox link.
import React from "react"
import {
Avatar,
Link,
ListItemAvatar,
ListItemButton,
styled
} from "#mui/material"
const ListItemButtonLink = styled(ListItemButton)(({ theme }) => ({
// ...here be styling
}))
interface LinkedListItemProps {
path: string
}
export const LinkedListItem = ({ path }: LinkedListItemProps) => {
return (
<ListItemButtonLink key={42} dense to={path} component={Link}>
<ListItemAvatar>
<Avatar>A</Avatar>
</ListItemAvatar>
Here Be Item
</ListItemButtonLink>
)
}
I'm totally out in the dark in how to get this working, as the Guide example doesn't pass Typescript checking either.
Looking through the MUI issues I found an issue that tackled the Guide issue, but doesn't really seem to fix it in a way that I could use.
I've also seen and read MUI button with styled-components including react router Link, but the solution is basically the non-Typescript guide version of a kind.
(updated)

You're using Link from the MUI package, this Link is just an anchor element but with better style integration with MUI theme, The Link you might want to use is from react-router which has a to prop, so change your code to:
import { Link } from "react-router-dom";
If you use styled and want the resulted component to have the prop to from react-router, you can add the generic type parameter to the HOC:
import { Link, LinkProps } from "react-router-dom";
const ListItemButtonLink = styled(ListItemButton)<LinkProps>(({ theme }) => ({
// ...here be styling
}));

Related

React & Material-UI – Custom theme within custom component

I have React app (functional components) and I use Material-UI. I have custom theme also and I use theme provider. I have created custom component (e.g. MySuperContainer) and I need to connect it with particular styles (object) in my theme
export const myTheme = { …, components: { MySuperContainer: { padding: “10px“, … } … }
I don’t want to import it to component to sx via useTheme hook. I don’t want to use styled components nor anything similar. Finally I want to write <MySuperContainer /> and it should already find its own custom styles in myTheme.
How can I achieve this? Is there any example or tutorial how to do this? I cannot find anything.

Re-exporting MUI components from React component library

I'm setting up a component library with React, Storybook, Typescript and Material UI. One of the main targets of the library is to re-export components that are imported from MUI (with a bit of config on top of them). I stumbled upon an issue where one of the components is not being rendered as intended when used in another React app. The component I am talking about is the Stepper component. Below is what I have now:
Stepper.tsx
import Stack from '#mui/material/Stack';
import MUIStepper, { StepperProps } from '#mui/material/Stepper';
const Stepper = (props: StepperProps) => {
return (
<Stack sx={{ width: '100%' }} spacing={4}>
<MUIStepper {...props}></MUIStepper>
</Stack>
);
};
export default Stepper
This is going to be built as a library using rollup.
I am not going to paste the entire rollup config here, but these are the plugins the config is using:
import babel from '#rollup/plugin-babel';
import resolve from '#rollup/plugin-node-resolve';
import commonjs from '#rollup/plugin-commonjs';
import postcss from 'rollup-plugin-postcss';
import filesize from 'rollup-plugin-filesize';
import autoprefixer from 'autoprefixer';
import typescript from "rollup-plugin-typescript2";
import dts from "rollup-plugin-dts";
After the process of building and publishing the library to an npm registry, I am trying to use it in some other React application, but some of the styles/internal components of the Stepper are totally missing (ex: active step css, step connectors etc.). The usage of the Stepper component is the same as in the official docs and it works perfectly with the original Stepper component.
Can you think of any configuration I am missing? It looks like something is lost along the way while building the library, but not sure what. Either that or the children components do not receive props properly.
I can provide further insight into this if necessary, but I didn't want to clutter the post anymore that it already is.
The Stepper component expects two or more Step components as children. To fix this, you need to pass props.children to the Stepper component.
import Stack from '#mui/material/Stack';
import MUIStepper, { StepperProps } from '#mui/material/Stepper';
const Stepper = (props: StepperProps) => {
return (
<Stack sx={{ width: '100%' }} spacing={4}>
<MUIStepper {...props}>{props.children}</MUIStepper>
</Stack>
);
};
export default Stepper;
Answering my own question here:
It looks like this behavior si encountered whenever we deal with components that use the Context API internally. Somehow, the context gets messed up if we use the root component from our library and other descendant components from MUI. Simply importing and re-exporting descendant components and using them from owr library as well fixes this issue, although I would want to avoid that. Until then, I created an issue on the github page to see if it is a bug on their side or just an intended behavior which affects my case here.
All in all, it is not really a rollup config issue. Transpilation and bundling work as intended.
Issue link: https://github.com/mui/material-ui/issues/33320

Material UI + styled with props throws "React does not recognize zIndex prop on a DOM element."

I'm struggling with React error -> React does not recognize zIndex prop on a DOM element.
It happens when I'm using MUI component and Styled with props.
Code example:
styled:
import {Drawer as DrawerBase, styled} from '#material-ui/core'
interface DrawerProps {
zIndex: number
}
export const Drawer = styled(DrawerBase)<Theme, DrawerProps>(({zIndex}) => ({
zIndex
}))
implementation:
<Drawer zIndex={10} />
I'm using:
"#material-ui/core": "^4.12.3"
Is there any possibility to prevent passing props to DOM elements?
Material UI's 'styled' syntax is very confusing unfortunately. This should work though:
interface DrawerProps {
zIndex: number
}
export const Drawer = styled(DrawerBase, {
shouldForwardProp: (prop) => prop !== 'zIndex',
})<Theme, DrawerProps>(({zIndex}) => ({
zIndex
}))
'shouldForwardProp' is also an unfortunate name as it sounds like the property zIndex isn't forwarded, but it is actually accessible.
Hope it helps.
I've not used styled components within MUI before, so take what I'm saying with a grain of salt. But I have a few ideas:
You're importing styled from '#material-ui/core', but you're calling a style function (without the "d").
Looking at the MUI docs for using styled components, they're using it the way we traditionally use styled components, which is using CSS syntax. I'd expect your code to look like this:
const Drawer = styled(DrawerBase)`
z-index: ${props => props.zIndex};
`;
The DOM element needs "z-index", not "zIndex".
Your code snippet also has a typo with your const Drawer where you've left out the "s": cont.
Hopefully this helps.

Storybook monorepo with Material UI fails to share theme across repos

I have a classic atoms/molecules/organisms monorepo with storybooks in order to create a custom components library. At the same time, such a library is based on MUI which has applied a custom theme. All of this is built with TypeScript.
I decided to base our library on MUI because it could be easier and faster for us to develop our custom components, but while I'm saving time on making them I'm putting the rest of my time trying to fix issues with the built :(
The theme
On atoms repo I have added our custom theme json which is imported by other repos (molecules and organisms) and it also gets exported as part of the atom's library and can be applied on an external project.
It uses a basic MUI as a base theme, which has been updated with some branding changes and also some custom colours (here is the problem). To make those custom colours available I have created a createPalette.d.ts file on the same Theme/ folder to declare the additions:
import '#mui/material/styles';
declare module '#mui/material/styles' {
interface Palette {
myColor: Palette['primary'];
}
interface PaletteOptions {
myColor?: PaletteOptions['primary'];
}
}
// Updated Button's props so it allows the color
declare module '#mui/material/Button' {
interface ButtonPropsColorOverrides {
myColor: true;
}
}
The them is then applied to each repo's .storybook/preview.js file like follows:
import { ThemeProvider as MUIThemeProvider, createTheme } from '#mui/material/styles';
import { ThemeProvider } from 'emotion-theming';
import theme from '../stories/Theme';
import { withThemes } from '#react-theming/storybook-addon';
const providerFn = ({ theme, children }) => {
// this fixes some issues between emotion and mui theming
const serialTheme = JSON.parse(JSON.stringify(theme));
const muiTheme = createTheme(serialTheme);
return (
<MUIThemeProvider theme={muiTheme}>
<ThemeProvider theme={muiTheme}>{children}</ThemeProvider>
</MUIThemeProvider>
);
};
const themingDecorator = withThemes(null, [theme], {
providerFn,
});
export const decorators = [themingDecorator];
And it all works just fine. I can make use of the MUI components and my theme easily.
Well, not exactly....
The problem
While I can access the theme from any other repo, it seems to fail to find the custom colours declaration and therefore every time I try to use something like theme.palette.myColor.main (as per our sample above) I get a
Property 'myColor' does not exist on type 'Palette'.ts(2339)
I have found out that, obviously, if I just replicate createPalette.d.ts on, let's say, molecules repo, it doesn't complain when I use it and also it autocomplete it with its main, dark, light... options. However, it does still through an error on 'build':
stories/ComponentFolder/index.tsx: error TS2339: Property 'myColor' does not exist on type 'Palette'.
My thoughts
What I think would be the most obvious solution is to import/extend createPalette.d.ts on any other repo but I don't seem to find a way of doing so, but also then there is the problem when building.
I'm not convinced this will work if I import the theme on any other project because maybe it would complain again about the property not existing on type 'Palette'.
I might as well just be doing this all wrong and the Theme should be stored and applied in a different way. What would be the best approach for something like this?
Notes
I'm also exporting the Theme as part of my library because I have also not been able to find a way of building and publishing my library with my theme applied. At the moment I am importing it: import theme from '#mylibrary/atoms/lib/Theme' and applying it with <ThemeProvider />. In some way, it's basically the same problem but instead of affecting other repos within my monorepo, it's affecting any other react app that tries to make use of the library.
I really, really hope someone can shed some light on this or share their way of creating something similar, please :D
I think that you are importing an interface and using it as a theme, pretty sure that you need to use a theme object like it is shown here in Using with Material-UI
const theme = {
palette: {
primary: {
main: '#556cd6',
},
secondary: {
main: '#19857b',
},
error: {
main: red.A400,
},
background: {
default: '#fff',
},
},
};

material-ui styles with next.js

I'm having trouble getting material-ui styles to work correctly with next.js. When I change a style and then save while running the dev server the styles get applied, but if I reload the page, save any other change, or restart the server the default material-ui styles overwrite my custom styles. Also, whenever I use the Box material-ui component I see this error in the console:
react-dom.development.js:67 Warning: Prop `className` did not match. Server: "MuiBox-root MuiBox-root-3 makeStyles-boxStyles-1" Client: "MuiBox-root MuiBox-root-4 makeStyles-boxStyles-1"
Here is my _document.tsx: https://pastebin.com/wJD9jyZQ
Here is my _app.tsx: https://pastebin.com/s8Ys01kb
Here is my index.tsx: https://pastebin.com/t5Z9QGpP
Here is index.styles.ts (where my custom styles are): https://pastebin.com/qe7M5ysq
In the _document.js file you need to enhance the App with the ServerStyleSheets object.
For Material UI v4 you need to import:
import { ServerStyleSheets } from '#material-ui/core/styles';
For Material UI v5 you need to import:
import { ServerStyleSheets } from '#material-ui/styles';
Then later down in the file:
const sheets = new ServerStyleSheets();
...
enhanceApp: (App: any) => (props) => sheets.collect(<App ... />),
See this v4 example
The above worked for me with v5 even though the v5 example did not include ServerStyleSheets

Resources